缘由

首先说明一下为什么会有这篇文章。前段时间,插件化以及热修复的技术很热,Nuwa热修复的工具NuwaGradle,携程动态加载技术DynamicAPK,还有希望做最轻巧的插件化框架的Small。这三个App有一个共同的地方就是大量的使用了Gradle这个强大的构建工具,除了携程的框架外,另外两个都发布了独立的Gradle插件提供自动化构建插件,或者生成热修复的补丁。所以学习一下Gradle插件的编写还是一件十分有意义的事。

插件类型

Gradle的插件一般有这么几种:

一种是直接在项目中的gradle文件里编写,这种方式的缺点是无法复用插件代码,在其他项目中还得复制一遍代码(或者说说复制一遍文件) 另一种是在独立的项目里编写插件,然后发布到中央仓库,之后直接引用就可以了,优点就是可复用。就和上面的Nuwa和Small一样。

Gradle相关语法

本篇文章不会详细说明Gradle相关的语法,如果要学习gradle相关的东西,请查看Gradle for Android

Gradle插件开发

Gradle插件是使用Groovy进行开发的,而Groovy其实是可以兼容Java的。Android Studio其实除了开发Android App外,完全可以胜任开发Gradle插件这一工作,下面来讲讲具体如何开发。

首先,新建一个Android项目。 之后,新建一个Android Module项目,类型选择Android Library。 将新建的Module中除了build.gradle文件外的其余文件全都删除,然后删除build.gradle文件中的所有内容。 在新建的module中新建文件夹src,接着在src文件目录下新建main文件夹,在main目录下新建groovy目录,这时候groovy文件夹会被Android识别为groovy源码目录。除了在main目录下新建groovy目录外,你还要在main目录下新建resources目录,同理resources目录会被自动识别为资源文件夹。在groovy目录下新建项目包名,就像Java包名那样。resources目录下新建文件夹META-INF,META-INF文件夹下新建gradle-plugins文件夹。这样,就完成了gradle 插件的项目的整体搭建,之后就是小细节了。目前,项目的结构是这样的。

打开Module下的build.gradle文件,输入

apply plugin: 'groovy'

apply plugin: 'maven'

dependencies {

compile gradleApi()

compile localGroovy()

}

repositories {

mavenCentral()

}

下面我们在包名下新建一个文件,命名为PluginImpl.groovy,注意有groovy后缀,然后在里面输入,注意包名替换为你自己的包名。

package cn.edu.zafu.gradle

import org.gradle.api.Plugin

import org.gradle.api.Project

public class PluginImpl implements Plugin{

void apply(Project project) {

project.task('testTask') << {

println "Hello gradle plugin"

}

}

}

然后在resources/META-INF/gradle-plugins目录下新建一个properties文件,注意该文件的命名就是你只有使用插件的名字,这里命名为plugin.test.properties,在里面输入

implementation-class=cn.edu.zafu.gradle.PluginImpl

注意包名需要替换为你自己的包名。

这样就完成了最简单的一个gradle插件,里面有一个叫testTask的Task,执行该task后会输出一段文字,就像当初我们输出HelloWorld一样。

发布到本地仓库

接着,我们需要将插件发布到maven中央仓库,我们将插件发布到本地仓库就好了,在module项目下的buidl.gradle文件中加入发布的代码。

repositories {

mavenCentral()

}

group='cn.edu.zafu.gradle.plugin'

version='1.0.0'

uploadArchives {

repositories {

mavenDeployer {

repository(url: uri('../repo'))

}

}

}

上面的group和version的定义会被使用,作为maven库的坐标的一部分,group会被作为坐标的groupId,version会被作为坐标的version,而坐标的artifactId组成即module名,我们让其取一个别名moduleName。然后maven本地仓库的目录就是当前项目目录下的repo目录。

这时候,右侧的gradle Toolbar就会在module下多出一个task

点击uploadArchives这个Task,就会在项目下多出一个repo目录,里面存着这个gradle插件。

目录就像上图这样,具体目录结构和你的包名等一系列有关,time是我的module名。喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPreisry1vbG+tdhtYXZlbrLWv+K686OsztLDx77NyrnTw8v8o6zU2r3QYXBwtcRhbmRyb2lkz+7Ev8/CtcRncmFkbGUuYnVpbGS1xM7EvP7W0LzTyOs8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;">

buildscript {

repositories {

maven {

url uri('../repo')

}

}

dependencies {

classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'

}

}

apply plugin: 'plugin.test'

apply plugin后面引号内的名字就是前文plugin.test.properties文件的文件名。而class path后面引号里的内容,就是上面grade中定义的group,version以及moduleName所共同决定的,和maven是一样的。

同步一下gradle,右侧app下other分类下就会多出一个testTask,双击执行这个Task,控制台就会输出刚才我们输入的字符串

发布到Jcenter仓库

接下来我们将其发布到jcenter中央仓库。参考之前写的一篇文章 使用Android Studio将开源库发布到Jcenter中央库

在项目根目录下的build.gradle文件中加入。

dependencies {

classpath 'com.android.tools.build:gradle:2.0.0-beta6'

classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'

classpath 'com.github.dcendents:android-maven-plugin:1.2'

}

在项目根路径下新建bintray.gradle文件,输入

apply plugin: 'com.jfrog.bintray'

apply plugin: 'maven-publish'

def projectName = "timePlugin"

def mavenDesc = 'your desc'

def baseUrl = 'https://github.com/yourBaseUrl'

def siteUrl = baseUrl

def gitUrl = "${baseUrl}/yourGitUrl"

def issueUrl = "${baseUrl}/yourGitIssueUrl"

def licenseIds = ['Apache-2.0']

def licenseNames = ['The Apache Software License, Version 2.0']

def licenseUrls = ['http://www.apache.org/licenses/LICENSE-2.0.txt']

def inception = '2016'

def username = 'lizhangqu'

install {

repositories {

mavenInstaller {

pom.project {

// Description

name projectName

description mavenDesc

url siteUrl

// Archive

groupId project.group

artifactId archivesBaseName

version project.version

// License

inceptionYear inception

licenses {

licenseNames.eachWithIndex { ln, li ->

license {

name ln

url licenseUrls[li]

}

}

}

developers {

developer {

name username

}

}

scm {

connection gitUrl

developerConnection gitUrl

url siteUrl

}

}

}

}

}

task sourcesJar(type: Jar) {

from sourceSets.main.allGroovy

classifier = 'sources'

}

task javadocJar(type: Jar, dependsOn: groovydoc) {

from groovydoc.destinationDir

classifier = 'javadoc'

}

artifacts {

archives javadocJar

archives sourcesJar

}

bintray {

user = BINTRAY_USER

key = BINTRAY_KEY

configurations = ['archives']

pkg {

repo = 'maven'

name = projectName

desc = mavenDesc

websiteUrl = siteUrl

issueTrackerUrl = issueUrl

vcsUrl = gitUrl

labels = ['gradle', 'plugin', 'time']

licenses = licenseIds

publish = true

publicDownloadNumbers = true

}

}

将对应的描述性文字修改为你自己的信息,尤其是最前面的一系列的def定义,然后在gradle.properties文件中加入BINTRAY_USER和BINTRAY_KEY。

在你的module中apply该grade文件

apply from: '../bintray.gradle'

右侧的gradle的toolbar就会多出几个task

之后我们先运行other下的install这个task,再执行bintrayUpload这个task,如果不出意外,就上传了,之后不要忘记到后台add to jcenter。成功add到jcenter之后就会有link to jcenter的字样

耐心等待add to center成功的消息,之后就可以直接引用了,将module下的gradle文件maven部分的定义

maven {

url uri('../repo')

}

前面加入

jcenter()

最终的内容如下

buildscript {

repositories {

jcenter()

maven {

url uri('../repo')

}

}

dependencies {

classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'

}

}

apply plugin: 'plugin.test'

就是这么简单,再次运行一下测试下是否成功。

最佳实践

最佳实践的来源是源自multidex,为什么呢,因为最近当方法数超了之后,如果选择multidex,编译的过程就会慢很多很多,为了检测到底是哪一步的耗时,需要编写一个插件来统计各个task执行的时间,因此就有了这么一个最佳实践。

在PluginImpl同级目录下新建TimeListener.groovy文件。输入

package cn.edu.zafu.gradle

import org.gradle.BuildListener

import org.gradle.BuildResult

import org.gradle.api.Task

import org.gradle.api.execution.TaskExecutionListener

import org.gradle.api.initialization.Settings

import org.gradle.api.invocation.Gradle

import org.gradle.api.tasks.TaskState

import org.gradle.util.Clock

class TimeListener implements TaskExecutionListener, BuildListener {

private Clock clock

private times = []

@Override

void beforeExecute(Task task) {

clock = new org.gradle.util.Clock()

}

@Override

void afterExecute(Task task, TaskState taskState) {

def ms = clock.timeInMs

times.add([ms, task.path])

task.project.logger.warn "${task.path} spend ${ms}ms"

}

@Override

void buildFinished(BuildResult result) {

println "Task spend time:"

for (time in times) {

if (time[0] >= 50) {

printf "%7sms %sn", time

}

}

}

@Override

void buildStarted(Gradle gradle) {}

@Override

void projectsEvaluated(Gradle gradle) {}

@Override

void projectsLoaded(Gradle gradle) {}

@Override

void settingsEvaluated(Settings settings) {}

}

然后将PluginImpl文件中的apply方法修改为

void apply(Project project) {

project.gradle.addListener(new TimeListener())

}

完成后打包发布到jcenter()。之后你只要引用了该插件,就会统计各个task执行的时间,比如运行app,就会输出像下面的信息。

最佳实践的末尾,推广一下这个插件,这个插件我已经将其发布到jcenter仓库,如果要使用的话加入下面的代码即可

buildscript {

repositories {

jcenter()

}

dependencies {

classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'

}

}

apply plugin: 'plugin.time'

传递参数

上面的是小试牛刀了下,接下来我们需要获得自定义的参数。

首先按照上面的步骤新建一个module。新建PluginExtension.groovy,输入

public class PluginExtension {

def param1 = "param1 defaut"

def param2 = "param2 defaut"

def param3 = "param3 defaut"

}

然后我们希望能传入嵌套的参数,再新建一个PluginNestExtension.groovy,输入

public class PluginNestExtension {

def nestParam1 = "nestParam1 defaut"

def nestParam2 = "nestParam2 defaut"

def nestParam3 = "nestParam3 defaut"

}

然后新建一个CustomTask.groovy,继承DefaultTask类,使用 @TaskAction注解标注实现的方法

public class CustomTask extends DefaultTask {

@TaskAction

void output() {

println "param1 is ${project.pluginExt.param1}"

println "param2 is ${project.pluginExt.param2}"

println "param3 is ${project.pluginExt.param3}"

println "nestparam1 is ${project.pluginExt.nestExt.nestParam1}"

println "nestparam2 is ${project.pluginExt.nestExt.nestParam2}"

println "nestparam3 is ${project.pluginExt.nestExt.nestParam3}"

}

}

只是做了拿到了参数,然后做最简单的输出操作,使用 ${project.pluginExt.param1}和 ${project.pluginExt.nestExt.nestParam1}等拿到外部的参数。

别忘了在META-INF/gradle-plugins目录下新建properties文件指定插件的接口实现类。

复制之前新建的PluginImpl.groovy到包下,修改apply方法

public class PluginImpl implements Plugin{

void apply(Project project) {

project.extensions.create('pluginExt', PluginExtension)

project.pluginExt.extensions.create('nestExt', PluginNestExtension)

project.task('customTask', type: CustomTask)

}

}

将插件发布到本地maven后,进行引用。

buildscript {

repositories {

maven {

url uri('../repo')

}

}

dependencies {

classpath 'cn.edu.zafu.gradle.plugin:test:1.0.0'

}

}

apply plugin: 'plugin.test'

定义外部参数,这里我们定义了param1,param2,nestParam1,nestParam2,此外param3和nestParam3保持默认。

pluginExt {

param1 = 'app param1'

param2 = 'app param2'

nestExt{

nestParam1='app nestParam1'

nestParam2='app nestParam2'

}

}

同步一下gradle,执行customTask。

上面的代码很简单,不用解释也能看到,所以不再解释了。

XM7 FOR ANDROID,如何使用Android Studio开发Gradle插件相关推荐

  1. Android Studio开发-高效插件强烈推荐

    Android Studio开发-高效插件强烈推荐 现在Android的开发者基本上都使用Android Studio进行开发(如果你还在使用eclipse那也行,毕竟你乐意怎么样都行).使用好And ...

  2. Android开发xposed,用Android Studio开发Xposed插件项目步骤

    1.使用Android Studio新建项目,在app目录下新建lib,并导入xposed的jar包. jar包下载地址:https://jcenter.bintray.com/de/robv/and ...

  3. Android原生整合虹软SDK开发uniapp插件

    1.项目背景 应公司要求,需要开发一套类似人脸打卡功能的app,但是因为我们公司没有很强的原生android开发者,所以根据现状选择了第三方跨平台的uniapp,想必目前大多人都了解这个平台了,我也就 ...

  4. Android studio 和 gradle插件版本对应关系(最新 2022年)

    官网链接:Android Gradle 插件版本说明  |  Android 开发者  |  Android Developers 由于网络的问题,在更新了Android studio版本之后,不能同 ...

  5. Android AOP编程(五)——Gradle插件+TransformAPI+字节码插桩实战

    开篇 在前面几篇博文中,我记录了Android AOP编程的一些基础知识,包括Gradle插件的开发.TransformAPI的使用,以及一些操作字节码的工具如AspectJ,Javassist和AS ...

  6. 使用Android studio 开发xposed插件

    1.在已经root的手机上安装xposed installer.apk 2.打开android studio 新建工程 如果不需要界面的 可以选择Add no activity 3.新建完成后,找到 ...

  7. android studio 开发实用插件

    1. JsonOnlineViewer android studio中调试接口数据 2. Sexy Editor 3. GsonFormat 4. CodeGlance 在右边可以预览代码,实现快速定 ...

  8. Android Studio的Gradle插件文档

    官方文档 http://google.github.io/android-gradle-dsl/current/

  9. Android Apk瘦身方案2——gradle插件将png自动压缩为webp

    实现思路 在 mergeRes 和 processRes 任务之间插入 WebP 压缩任务,如下图所示: 使用开源框架Cwebp,使用命令行对所有的图片进行遍历处理,然后将结果输出 Google 官方 ...

最新文章

  1. 制作测试MM32F3277-MicroPython最小电路板
  2. Retrofit 原理解析
  3. 关于学生信息录入(文件操作)的心得体会
  4. 【机器学习】总结机器学习3个时代的算力规律:大模型的出现改变了什么?
  5. [转]SQL Server 索引基础知识(2)----聚集索引,非聚集索引
  6. 10、java中文件的抽象表示
  7. linux-vim编辑器简览
  8. 【matplotlib笔记】plt.subplot()绘制子图
  9. 英文的写作 —— 词汇的积累(环境的描写、写人)
  10. 数据切分——MySql表分区概述
  11. 微服务之间最佳调用方式是什么?
  12. 深入学习c++(虚函数遇到析构函数就退化了)
  13. raspberry pi4B ncnn cpu vulkan benchmark
  14. 音频编码格式——AAC简介
  15. Ubuntu12.04软件安装指南
  16. IDEA 类中找不到main方法请将main方法定义为public static void main. 否则 JavaFX 应用程序类必须扩展javafx.application.Applicati
  17. map-box地图点击图标高亮事件
  18. 高速缓存DNS解析详解
  19. php-pfm并发,php-pfm配置详解
  20. 成立大数据公司 新华三“数据引擎”要干些什么事?

热门文章

  1. oracle parameterfile
  2. IncrediBuild 加速原理
  3. Attribute和Property的区别
  4. web服务器中启用作业储存_如何在Kubernetes中启用无服务器计算
  5. 程序员里面开源_如何以开源程序员的身份开始
  6. 对等通信_新的通信技术如何影响对等参与
  7. opensource项目_最佳Opensource.com:教程
  8. x264的交叉编译和移植
  9. Bootstrap3 带列表组的面板
  10. es6 import 命令