在AndroidStudio中实现Gradle自定义插件

  • 内容
  • 评论
  • 相关

1.  前言

Android Studio 项目是使用 Gradle 构建的,构建工具 Gradle 可以看做是一个脚本,包含一系列的Task,依次执行这些 Task 后,项目就打包成功了。
而 Task 有一个重要的概念,那就是 inputs 和outputs 。
Task 通过 inputs 拿到一些东西,处理完毕之后就输出 outputs ,而下一个 Task 的 inputs 则是上一个 Task 的outputs。
例如:一个 Task 的作用是将 java 编译成 class,这个 Task 的 inputs 就是 java 文件的保存目录,outputs 这是编译后的 class 的输出目录,它的下一个 Task 的 inputs 就会是编译后的 class 的保存目录了。
Gradle 由一个个 Task 组成,而这些 Task 都是由 Plugin 来定义的。
比如:
apply plugin : ‘com.android.application’ 这个 插件定义了将 Module 编译成 application 的一系列 Task。
apply plugin : ‘com.android.library’ 这个 插件定义了将 Module 编译成 library 的一系列 Task。
不同的 Plugin 提供了不同的 Task 来实际不同的功能。
可以简单的理解为: Gradle只是一个框架,真正起作用的是plugin。而plugin的主要作用是往Gradle脚本中添加Task
所以,我们自己写插一个插件也是这样引入,引入后我们的插件就会得到执行。有人就好问,为啥非得自己编写一个插件?

2. 自定义插件有何用

  1. 动态替换资源文件,满足不同渠道的需求。
  2. 动态注入或修改代码。
  3. 是实现热修补的关键一步。

3. 自定义插件的方法

在Gradle中创建自定义插件,Gradle提供了三种方式:
  1. 在build.gradle脚本中直接使用
  2. 在buildSrc中使用
  3. 在独立Module中使用
开发Gradle插件可以在IDEA中进行开发,也可以在Android Studio中进行开发,它们唯一的不同,就是IDEA提供了Gradle开发的插件,比较方便创建文件和目录,而Android Studio中,开发者需要手动创建(但实际上,这些目录并不多,也不复杂,完全可以手动创建)。

4. 自定义插件的实战—在buildSrc中使用

在Android Studio中创建一个标准的Android项目,整个目录结构如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
├── app
│   ├── build.gradle
│   ├── libs
│   └── src
│       ├── androidTest
│       │   └── java
│       ├── main
│       │   ├── AndroidManifest.xml
│       │   ├── java
│       │   └── res
│       └── test
├── build.gradle
├── buildSrc
│   ├── build.gradle            ---1
│   └── src
│       └── main
│           ├── groovy          ---2
│           └── resources       ---3
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
其中,除了buildSrc目录以外,都是标准的Android目录,而buildSrc就是Gradle提供的在项目中配置自定义插件的默认目录,开发Gradle要创建的目录,也就是RootProject/src/main/groovy和RootProject/src/main/resources两个目录。
在配置完成后,如果配置正确,对应的文件夹将被IDE所识别,成为对应类别的文件夹。

4.1 创建buildSrc/build.gradle

首先,先来配置buildSrc目录下的build.gradle文件,这个配置比较固定,脚本如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apply plugin: 'groovy'
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //build tools
    //implementation 'com.android.tools.build:gradle:3.1.3'
    //gradle sdk
    implementation gradleApi()
    //groovy sdk
    implementation localGroovy()
    //transform
    implementation 'com.android.tools.build:transform-api:2.0.0-deprecated-use-gradle-api'
    //javassist
    implementation 'org.javassist:javassist:3.23.1-GA'
    //commons-io
    implementation 'commons-io:commons-io:2.6'
}
 
repositories {
    google()
    jcenter()
    mavenCentral()
}

4.2 创建Groovy脚本MyPlugin.groovy

在groovy目录下,创建一个Groovy类(与Java类似,可以带包名,但Groovy类以.grovvy结尾)
在脚本中通过实现gradle的Plugin接口,实现apply方法即可,脚本如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.aoaoyi.plugin.gradle
 
import org.gradle.api.Plugin
import org.gradle.api.Project
 
class MyPlugin implements Plugin {
 
    void apply(Project project) {
 
        project.task('testPlugin').doLast {
            println "Hello aoaoyi gradle plugin in pluginSrc"
        }
    }
 
}
在如上所示的脚本的apply方法中,我简单的实现了一个task,命名为testPlugin,执行该Task,会输出一行日志。

4.3 创建Groovy脚本Extension

所谓Groovy脚本的MyExtension,实际上就是类似于Gradle的配置信息,在主项目使用自定义的Gradle插件时,可以在主项目的build.gradle脚本中通过Extension来传递一些配置、参数。
创建一个MyExtension,只需要创建一个Groovy类即可,我在这里命名了一个叫MyExtension.groovy类,其脚本如下所示:
package com.aoaoyi.plugin.gradle

class MyExtension {
    String message
}
MyExtension代码非常简单,就是定义了要配置的参数变量,下面将具体演示如何使用。

4.5 在Groovy脚本中使用Extension

在创建了Extension之后,需要修改下之前创建的Groovy类来加载Extension,修改后的脚本如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.aoaoyi.plugin.gradle
 
import org.gradle.api.Plugin
import org.gradle.api.Project
 
class MyPlugin implements Plugin {
 
    void apply(Project project) {
 
        project.extensions.create('pluginSrc', MyExtension)
 
        project.task('testPlugin').doLast {
            println project.pluginSrc.message
        }
    }
 
}
通过project.extensions.create方法,来将一个Extension配置给Gradle即可。

4.6 创建resources

resources目录是标识整个插件的目录,其目录下的结构如下所示:
1
2
3
└── resources 
     └── META-INF 
          └── gradle-plugins
该目录结构与buildSrc一样,是Gradle插件的默认目录,不能有任何修改。创建好这些目录后,在gradle-plugins目录下创建——插件名.properties文件
如上所示,这里我命名为plugin-aoaoyi.properties,在该文件中,代码如下所示:
1
implementation-class=com.aoaoyi.plugin.gradle.MyPlugin
通过上面的代码指定最开始创建的Groovy类即可。

4.7 在主项目中使用插件

在主项目的build.gradle文件中,通过apply指令来加载自定义的插件,脚本如下所示:
1
2
3
4
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'plugin-aoaoyi'
其中plugin的名字,就是前面创建plugin-aoaoyi.properties中的名字——plugin-aoaoyi,通过这种方式,就加载了自定义的插件。

4.8 配置Extension

在主项目的build.gradle文件中,通过如下所示的代码来加载Extension:
1
2
3
pluginSrc{
    message = 'hello aoaoyi gradle plugin'
}
注意:pluginSrc{……}在app.build中是和android{……},dependencies{……}同一级。
同样,领域名为插件名,配置的参数就是在Extension中定义的参数名。
配置完毕后,就可以在主项目中使用自定义的插件了,在终端执行gradle testPlugin指令,结果如下所示:
1
2
3
4
5
6
控制台输入执行命令:
gradlew :app:testPlugin
 
控制台输出执行结果
> Task :app:testPlugin 
hello aoaoyi gradle plugin

5. 自定义插件的实战—在独立Module中使用

在buildSrc中创建自定义Gradle插件只能在当前项目中使用,因此,对于具有普遍性的插件来说,通常是建立一个独立的Module来创建自定义Gradle插件。

5.1 创建Android Library Module

首先,在主项目的工程中,创建一个普通的Android Library Module,并删除其默认创建的目录,修改为Gradle插件所需要的目录,即在buildSrc目录中的所有目录,如图所示:
如上图所示,创建的文件与在buildSrc目录中创建的文件都是一模一样的,只是这里在一个自定义的Module中创建插件而不是在默认的buildSrc目录中创建。
需要注意的是,这里我命名为com.aoaoyi.plugin.properties,在该文件中,代码如下所示:
1
implementation-class=com.aoaoyi.plugin.AoPlugin

5.2 部署到本地Repo

因为是通过自定义Module来创建插件的,因此,不能让Gradle来自动完成插件的加载,需要手动进行部署,所以,需要在插件的build.gradle脚本中增加Maven的配置,脚本如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
apply plugin: 'groovy'
apply plugin: 'maven'
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //build tools
    //implementation 'com.android.tools.build:gradle:3.1.3'
    //gradle sdk
    implementation gradleApi()
    //groovy sdk
    implementation localGroovy()
    //transform
    implementation 'com.android.tools.build:transform-api:2.0.0-deprecated-use-gradle-api'
    //javassist
    implementation 'org.javassist:javassist:3.23.1-GA'
    //commons-io
    implementation 'commons-io:commons-io:2.6'
}
 
repositories {
    google()
    jcenter()
    mavenCentral()
}
 
group='com.aoaoyi.plugin.tools'
version='2.0.0'
uploadArchives {
    repositories {
        mavenDeployer {
            //提交到远程服务器:
            // repository(url: "http://www.aoaoyi.com/repos") {
            //    authentication(userName: "admin", password: "admin")
            // }
            //本地的Maven地址设置为project目录下
            repository(url: uri('../../repo'))
        }
    }
}
相比buildSrc中的build.gradle脚本,这里增加了Maven的支持和uploadArchives这样一个Task,这个Task的作用就是将该Module部署到本地的repo目录下。在终端中执行gradle uploadArchives指令或通过IDE执行。
将插件部署到repo目录下,如图所示:
当插件部署到本地后,就可以在主项目中引用插件了。
当插件正式发布后,可以把插件像其它module一样发布到中央库,这样就可以像使用中央库的库项目一样来使用插件了。

5.3 在主项目中使用插件

在buildSrc中,系统自动帮开发者自定义的插件提供了引用支持,但自定义Module的插件中,开发者就需要自己来添加自定义插件的引用支持。在项目级的build.gradle文件中,添加如下所示的脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
buildscript {
 
    repositories {
        maven {
            url uri('/repo')
        }
        google()
        jcenter()
    }
 
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.aoaoyi.plugin.tools:buildPlugin:2.0.0'
    }
}
其中,classpath指定的路径,就是类似compile引用的方式,即——插件名:group:version
在主项目APP的build.gradle文件中,通过apply指令来加载自定义的插件,脚本如下所示:
1
2
3
4
5
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'plugin-aoaoyi'
apply plugin: 'com.aoaoyi.plugin'
其中plugin的名字,就是前面创建com.aoaoyi.plugin.properties中的名字——com.aoaoyi.plugin,通过这种方式,就加载了自定义的插件。
在主项目的build.gradle文件中,通过如下所示的代码来加载Extension:
1
2
3
aoaoyiPlugin{
    message = 'hello aoaoyi gradle plugin tools'
}
注意:aoaoyiPlugin{……}在app.build中是和android{……},dependencies{……}同一级。
同样,领域名为插件名,配置的参数就是在Extension中定义的参数名。
配置完毕后,就可以在主项目中使用自定义的插件了,在终端执行gradle testPlugin指令,结果如下所示:
1
2
3
4
5
6
控制台输入执行命令:
gradlew :app:testAoaoyiPlugin
 
控制台输出执行结果
> Task :app:testAoaoyiPlugin 
hello aoaoyi gradle plugin tools
如果不使用本地Maven Repo来部署,也可以拿到生成的插件jar文件,复制到libs目录下,通过如下所示的代码来引用:
1
implementation fileTree(dir: 'libs', include: ['*.jar']) // 使用jar
推荐:
Android批量打包