谈到Gradle开发,相信很多人第一反应就是开发Gadle Plugin
。这倒也没错,plugin
是Gradle提供出来,通过apply(Closure closure)
方法就可以轻松注入到编译过程中来。
当然,你可以说就开发一个Task
类,然后在gradle
脚本中引入这个Task
,这也是可行的,只不过看起来就似乎没有那么优雅以及显得封装性不是那么完整。
PluginAware
谈起plugin
那不可避免就要提到org.gradle.api.plugins.PluginAware
,为什么这样说呢?因为,它的含义是可以apply plugin
的所在,换句话说只有实现了PluginAware
的对象才可以apply plugin
。
从上图可以得知,gradle中有两个子接口继承PluginAware,分别是org.gradle.api.Project
和org.gradle.api.initialization.Settings
,当然这两个接口也有分别实现的默认类,是对这两个接口的具体实现,暂不延伸去讲了。
因此可知,有两种类型插件,分别是:
Project[build.gradle]
class ProjectPlugin implements Plugin<Project>{
void apply(Project target){
...
}
}
Settings[settings.gradle]
class SettingsPlugin implements Plugin<Settings>{
void apply(Settings target){
...
}
}
由此可以根据不同的需求(例如settings才能添加顶级工程的classpath
依赖)开发不同类型的Plugin
。
PluginManager
对,从字面意思来看-插件管理器。无论是Project
还是Settings
,都是通过org.gradle.api.plugins.PluginManager
来对Plugin
来进行具体操作的,其中最核心的方法如下。
void apply(String pluginId);
AppliedPlugin findPlugin(String id);
boolean hasPlugin(String id);
void withPlugin(String id, Action<? super AppliedPlugin> action);
很简单但是也最常用,apply
是应用插件,findPlugin
和hasPlugin
分别是查找插件获得插件信息以及判断插件是否被应用,而withPlugin
是表示某个插件被应用的时候执行相应的Action
回调,其中方法中参数无论是id
还是pluginId
都是指插件的id
。
Plugin
Structure
Plugin
通常是作为Library
进行开发,工程结构如下:
-
Gradle Plugin
需要在resources--METAINF--gradle-plugins
目录下面定义一个plugin_id.properties
文件,文件里面内容就是具体的Plugin
实现类的全路径。为什么要这样做呢,其实这是借鉴了Java
的SPI(Service Provider Interface)
机制,使得整个Plugin
的加载更加优雅和解耦。 - 另外,当插件用
groovy
和kotlin
进行混合编码的时候,需要注意的是groovy
和kotlin
的源码是由各自的编译task
进行编译的,因此在相互引用的时候会出现编译找不到类,所以需要在对应的CompileXXTask
中的classPath
添加编译后的类路径,由于kotlin
是在groovy
之前编译的,所以CompileGroovyTask
可以添加kotlin
的类路径,反之则不行,这意味着只能groovy
引用kotlin
的代码。 - 最后
Plugin
开发常规有两种方式,分别是通过在工程里面定义buildSrc module
或者常规的module
来进行开发。
至于具体的开发细节,这里就不再细讲了,因为就是在
void apply(Project target){
//todo something
...
}
中开发自己的编译逻辑,具体API
可以自行查阅官方文档。
Module
这里单独说一下Plugin
开发的Module
,绝大多数介绍基本就是说可以用buildSrc
或者独立module
的方式进行开发,但是两者有什么优缺点,如何进行选择,都没有给出相应的解释,因此在这里做一个简单的总结。
这两种方式具体表现是在:
-
buildSrc module
是在整个Project
进行configuration
前会对这个module
进行编译并且把编译后的jar
自动加入到Project
的classpath
中,直接就可以进行调试。 - 独立
module
的方式相对会麻烦一点,每次需要通过uploadArchives
将jar
发布到本地,然后手动在顶级工程的build.gradle
将jar
依赖添加到classpath
中。
那么,既然第二种调试方式会更加繁琐,是不是第一种方式最好了。其实也不尽然,这两种方式有着各自的优缺点。
- 第一种方式因为它是在
Project
进行configuration
前就会进行单独的编译,那么就意味着它是一个独立的module
,不能去依赖工程里面其他的module
,它在单独进行编译的时候其他的module
是找不到的,也就意味着它是拿不到Project
里面所有的gradle
编译信息的。 - 第二种作为独立的
module
进行开发的方式,则可以跟正常的module
开发一样引用工程里面的其他module
。
所以,这两种方式各有取舍,需要按照自己的需求来进行选择。
Extension
Plugin
开发肯定也会需要用到外界的数据,那怎么得到所要的数据呢?前面提到org.gradle.api.Plugin
接口的void apply(T target)
方法,对应的泛型对象在gradle
中分别是Project
和 Settings
,那么可以通过Property
进行数据的传递。那还有没有更加优雅的方式呢?没错,那就是Extension
。
ExtensionContainer
通过Project
的getExtensions
方法获得ExtensionContainer
对象,然后通过create
方法来创建并且添加对应的Extension
。
<T> T create(String name, Class<T> type, Object... constructionArguments);
方法对应的参数分别是extension
的名称、用来装载的class
以及构造对应实例的时候所需要传入的参数。
另外ExtensionContainer
还有findByType
、getByType
等方法,可以自行查阅org.gradle.api.plugins.ExtensionContainer
。
那么,标准的extension
创建的方法如下:
- 定义一个类似于Bean的
Extension
的装载类
class SampleExtension{
String id = ""
}
- 插件中创建对应的
Extension
void apply(Project target){
target.extensions.create("sample", Sample)
}
- 在对应
apply Plugin
的build.gradle
中传值
sample{
id = "smapleExtension_test"
}
- 插件中读取
extension
project.afterEvaluate{
SampleExtension sampleExtension = project.sample
//打印出来就是传值smapleExtension_test
println "sampleExtension.id-----${sampleExtension.id}"
}
有几个关键点需要注意下:
-
afterEvaluate
,也就是project
完成整个配置评估后,才会把配置的值装载到对应的extension
类中,因此这个时候才能拿到对的值。 - 在
build.gradle
进行extension
传值,闭包名称必须和create
方法中传入的name
保持一致。 - 获取对应的
extension
对象,直接通过project.sample
也就是闭包名称即可。
另外,我们也可以通过相同的方式在自己的插件中去获取别的插件对应的extension
。例如:
project.android.registerTransform("...")
这是Plugin Transform
开发中必须要用到的,就是通过project.android
拿到对应的extension
类,这里就是com.android.build.gradle.BaseExtension
对象,然后调用其public void registerTransform(@NonNull Transform transform, Object... dependencies)
方法。
Andoird Extension
在Android
开发中,用到最多就是Gradle
提供出来的三个插件:
-
com.android.build.gradle.AppPlugin
,对应app module
中的apply plugin: 'com.android.application'
。 -
com.android.build.gradle.LibraryPlugin
,对应aar module
中的apply plugin: 'com.android.library'
。 -
org.gradle.api.plugins.JavaPlugin
,对应java module
中的apply plugin: 'java'
。
当然,如果用到了kotlin
,那么还需要用到对应的kotlin plugin
。
这里,着重说一下AppPlugin
以及LibraryPlugin
这两个插件,这两个插件对应的extension
分别是com.android.build.gradle.AppExtension
以及com.android.build.gradle.LibraryExtension
,继承于com.android.build.gradle.TestedExtension
。
TestedExtension
是用于android的test module,继承于所有android插件的基础extension也就是com.android.build.gradle.BaseExtension
。
里面包含的主要方法有:
public void compileSdkVersion(int apiLevel)
public void buildToolsVersion(String version)
public void defaultConfig(Action<DefaultConfig> action)
public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action)
public void compileOptions(Action<CompileOptions> action)
...
public void sourceSets(Action<NamedDomainObjectContainer<AndroidSourceSet>> action)
是不是感觉很熟悉,再来看下常见的build.gradle
中的配置:
android {
compileSdkVersion "${compile_sdk_version}" as Integer
buildToolsVersion "${build_tools_version}"
defaultConfig {
minSdkVersion "${min_sdk_version}" as Integer
targetSdkVersion "${target_sdk_version}" as Integer
versionCode Integer.valueOf(System.env.BUILD_NUMBER ?: "1")
versionName "${version}"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
sourceSets {
main {
java {
include '**/*.java'
include '**/*.kt'
}
}
}
}
应该发现了android
闭包里面所有的配置选项对应的就是上述extension
中的方法,compileSdkVersion
、buildToolsVersion
、defaultConfig
、compileOptions
以及sourceSets
都是一一对应的。
另外defaultConfig
闭包配置选项对应的是com.android.build.gradle.internal.dsl.DefaultConfig
类属性,compileOptions
闭包配置对应的就是com.android.build.gradle.internal.CompileOptions
,其他以此类推。
由此得知,android{}
闭包里面有什么配置选项,直接查看对应的AppExtension
和LibraryExtension
中的方法和属性就好了,其他插件也是如此。
尾结
至此,Plugin
的开发介绍就到此为止了,在这章介绍了Plugin
的由来、开发module
差异以及最重要的Extension
。好了,下章来了解下Gradle
中另外一个重要的部分publishing
。