缘由

首先说明一下为什么会有这篇文章。前段时间,插件化以及热修复的技术非常热,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插件这一工作,以下来讲讲详细怎样开发。

  1. 首先,新建一个Android项目。
  2. 之后。新建一个Android Module项目,类型选择Android Library。
  3. 将新建的Module中除了build.gradle文件外的其余文件全都删除,然后删除build.gradle文件里的全部内容。

  4. 在新建的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<Project> {
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名。

公布到本地maven仓库后,我们就使用它,在叫app的android项目下的gradle.build的文件里增加

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 %s\n", 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<Project> {
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。

上面的代码非常easy,不用解释也能看到,所以不再解释了。

參考文章

源代码

最后上本篇文章的源代码

http://download.csdn.net/detail/sbsujjbcy/9451566

怎样使用Android Studio开发Gradle插件的更多相关文章

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

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

  2. 【Android Studio安装部署系列】二十四、Android studio中Gradle插件版本和Gradle版本关系

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 在从Android Studio3.0.0版本升级到Android Studio3.0.1版本的时候,出现了一个问题,需要升级Gra ...

  3. Android Studio 更新gradle插件

    今天更新了CentOS, 更新了java版本. 然后gradle跪了..... 不吐槽java版本的兼容性问题了.... 反正有他自己的理由.... 那么就更新gradle咯.... 下面是方法... ...

  4. Android Studio开发环境配置以及相关说明

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 这里简单记录下在开发的时候使用的Android Studio开发环境版本以及相关注意事项. 一般来讲,每隔一段时间就要检查下Andr ...

  5. 49个你应该了解的Android Studio技巧、插件与资源 http://www.apkbus.com/blog-822721-72630.html (出处: 安卓巴士 - 安卓开发 - Android开发 - 安卓 - 移动互联网门户)

    49个你应该了解的Android Studio技巧.插件与资源http://www.apkbus.com/blog-822721-72630.html(出处: 安卓巴士 - 安卓开发 - Androi ...

  6. 使用 Android Studio 开发 widget 安卓桌面插件

    •What AppWidget 即桌面小部件,也叫桌面控件,就是能直接显示在Android系统桌面上的小程序: 这么说可能有点抽象,看图: 像这种,桌面上的天气.时钟.搜索框等等,都属于 APP Wi ...

  7. 使用Android Studio开发J2SE项目方法

    0.前言 最近因为要为项目开发一个底层的Java应用,所以非常偶然的遇到了这样一个问题,过去Eclipse有Java Project而现在手头使用Android Studio并不能直接建立Java应用 ...

  8. 快速掌握 Android Studio 中 Gradle 的使用方法

    快速掌握 Android Studio 中 Gradle 的使用方法 Gradle是可以用于Android开发的新一代的 Build System, 也是 Android Studio默认的build ...

  9. 【Android 应用开发】Ubuntu 下 Android Studio 开发工具使用详解 (旧版本 | 仅作参考)

    . 基本上可以导入项目开始使用了 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21035637 ...

随机推荐

  1. eclipse中删除多余的tomcat server

    在eclipse菜单中选择Window--Show View--Server--Servers,打开这个服务窗口,将多余的服务删除即可. 如果每次启动中太卡没反应,那就是服务没选择好,或是端口冲突的原 ...

  2. SVN提交文件的时候过滤指定文件

    如果使用TortoiseSVN作为客户端的话,可以在TortoiseSVN右键菜单中的 "设置"(settings)--常规设置(General)--全局忽略样式里设置(Globa ...

  3. Spring IOC容器分析(2) -- BeanDefinition

    上文对Spring IOC容器的核心BeanFactory接口分析发现:在默认Bean工厂DefaultListableBeanFactory中对象不是以Object形成存储,而是以BeanDefin ...

  4. 四:java调接口实现发送手机短信验证码功能

    1.点击获取验证码之前的样式: 2.输入正确的手机号后点击获取验证码之后的样式: 3.如果手机号已经被注册的样式: 4.如果一个手机号一天发送超过3次就提示不能发送: 二:前台的注册页面的代码:reg ...

  5. 二:Redis快速入门及应用

    Redis的使用难吗?不难,Redis用好容易吗?不容易.Redis的使用虽然不难,但与业务结合的应用场景特别多.特别紧,用好并不容易.我们希望通过一篇文章及Demo,即可轻松.快速入门并学会应用. ...

  6. C#操作Excel(读取)

    一.使用OleDb,这个法子好像不大好使.容易读错.引用System.Data.OleDb;     /**//// <summary>        /// 返回Excel数据源     ...

  7. c++ const全局对象是如何处理的

    我主要是记录一个发现,目前我不能解释,先作个记录. const 只是一个 语义约束,由编译器强制实施的.使被约束的对象不能被直接访问修改. 我用 『直接』这词,因为在代码段中 ,用一个const 指针 ...

  8. Linux下gcc编译生成动态链接库*.so文件并调用它

    动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一 ...

  9. keras 修仙笔记一

    对于牛逼的程序员,人家都喜欢叫他大神:因为大神很牛逼,人家需要一个小时完成的技术问题,他就20分钟就搞定.Keras框架是一个高度集成的框架,学好它,就犹如掌握一个法宝,可以呼风唤雨.所以学keras ...

  10. web-php绕过

    0x01.web-PHP的悖论1 题目: 链接:http://game.sycsec.com:2009/10111.php 解题思路: 1.首先,web对于选择二进制方向的我这个菜鸡绝对是十分懵逼的, ...