前言
逐步整理的一系列的总结:
Android 自定义Gradle插件的Extension类(五)
Android Gradle 中的使用ASMified插件生成.class的技巧(九)
Android Gradle 中的实例之动态修改AndroidManifest文件(十)
在 Android 自定义Gradle插件的完整流程(三)中的3.Task中的属性扩展中主要是仅仅一层属性扩展,在Android Gradle中的android{}在进行配置的时候,通常如下
从示例中通常在android{}里面还有一层defaultConfig{}的配置,那么在自定义扩展属性的时候,如果定义含有多层闭包的配置属性呢?
一 自定义扩展多层属性
- 1.自定义属性扩展类AndroidExtension
因为现在是两层属性扩展,有两层闭包,那么其实每层闭包就对应属性扩展类的一个类,那么该自定义属性扩展类如下:
public class AndroidExtension {
public static final String TAG = "androidExtension";
private String compileSdkVersion;
private String buildToolsVersion;
private DefaultConfig defaultConfig = new DefaultConfig();
public void setCompileSdkVersion(String version) {
this.compileSdkVersion = version;
}
public String getCompileSdkVersion() {
return this.compileSdkVersion;
}
public void setBuildToolsVersion(String version) {
this.buildToolsVersion = version;
}
public String getBuildToolsVersion() {
return this.buildToolsVersion;
}
public void setDefaultConfig(Closure config) {
ConfigureUtil.configure(config, defaultConfig);
}
public DefaultConfig getDefaultConfig() {
return defaultConfig;
}
class DefaultConfig {
private String applicationId;
private String minSdkVersion;
public void setApplicationId(String id) {
this.applicationId = id;
}
public String getApplicationId() {
return this.applicationId;
}
public void setMinSdkVersion(String sdk) {
this.minSdkVersion = sdk;
}
public String getMinSdkVersion() {
return this.minSdkVersion;
}
}
}
其中AndroidExtension类就对应着最外层的闭包,内部类DefaultConfig就对应着里面的一层闭包,而内部类DefaultConfig对应的闭包就是最外层AndroidExtension类的一个属性defaultConfig。
特别注意的是对于内部类DefaultConfig对应的属性defaultConfig的赋值,通常有两种实现方式:
第一种方式:传入参数为Action,代码如下:
public void setDefaultConfig(Action<DefaultConfig> action) {
action.execute(defaultConfig);
}
这种方式在build.gradle文件配置该属性的时候 ,此时的defaultConfig的属性就是一个闭包,如下:
androidExtension {
compileSdkVersion = "1.0.0"
buildToolsVersion = "29.0.0"
defaultConfig {
it.applicationId = "1.0.0"
it.minSdkVersion = "3.0.0"
}
}
第二种方式:传入参数是一个Closure,代码如下:
public void setDefaultConfig(Closure config) {
ConfigureUtil.configure(config, defaultConfig);
}
这种方式在build.gradle文件配置该属性的时候 ,此时的defaultConfig的属性就是一个闭包,如下:
androidExtension {
compileSdkVersion = "1.0.0"
buildToolsVersion = "29.0.0"
defaultConfig {
applicationId = "1.0.0"
minSdkVersion = "3.0.0"
}
}
当然除了可以通过setDefaultConfig()的方式来对该属性进行扩展,还可以衍生出第三种方式:直接使用属性名作为方法名来对属性进行赋值,如下:
public void defaultConfig(Action<DefaultConfig> action) {
action.execute(defaultConfig);
}
并且还发现这里如果采用这种方式进行对属性赋值,那么在build.gradle文件进行配置该属性的时候 ,可以直接对属性进行赋值,而不需要像第一种方式那样还要通过it.applicationId进行赋值。(遗留问题:为什么会是这个样子呢?)
defaultConfig {
applicationId = "1.0.0"
minSdkVersion = "3.0.0"
}
- 2.将 AndroidExtension添加到ExtensionContainer中
class FirstPluginProject implements Plugin<Project> {
@Override
void apply(Project project) {
.........
//测试两层闭包
createAndroidExtensions(project)
testAndroidExtension(project)
}
void createAndroidExtensions(Project project) {
project.getExtensions().create(AndroidExtension.TAG, AndroidExtension)
}
}
- 3.通过project.getExtensions().findByName找到对应的配置类
void testAndroidExtension(Project project) {
project.afterEvaluate {
AndroidExtension extension = project.getExtensions().findByName(AndroidExtension.TAG)
SystemOutPrint.println("compileSdkVersion = " + extension.compileSdkVersion)
SystemOutPrint.println("applicationId = " + extension.defaultConfig.getApplicationId())
SystemOutPrint.println("minSdkVersion = " + extension.defaultConfig.minSdkVersion)
}
}
将该插件打包发布, 在Android Studio中运行项目,在Build输出窗口中的输出内容如下:
> Configure project :app
<<<<<<<settings.gradle<<<<<<<<<< beforeProject Project name = app
%%%%%%%%% FirstPluginProject %%%%%%%%% compileSdkVersion = 1.0.0
%%%%%%%%% FirstPluginProject %%%%%%%%% applicationId = 1.0.0
%%%%%%%%% FirstPluginProject %%%%%%%%% minSdkVersion = 3.0.0
具体的代码已经上传到至https://github.com/wenjing-bonnie/AndroidPlugin.git的firstplugin目录的相关内容。因为逐渐在这基础上进行迭代,可回退到Gradle_5.0该tag下可以查看相关内容。
二 可以自定义名字的属性扩展
在Android Gradle配置中还有一些可以自定义名字的属性扩展,如:productFlavors {}(见Android Gradle中的productFlavors)、buildTypes{}等。这些都是可以在里面自定义名字对属性进行扩展。那么在自定义Gradle插件的时候,如何实现呢?
在Gradle中提供了NamedDomainObjectContainer来解决这个问题。接上面那部分继续增加。
- 1.在AndroidExtension定义属性扩展类BuildTypes
static class BuildTypes {
private boolean signingConfig;
//必须含有name属性,否则会抛出"'com.wj.plugin.extension.AndroidExtension$BuildTypes@130b3f7d' because it does not have a 'name' property"
private String name;
BuildTypes(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setSigningConfig(boolean config) {
this.signingConfig = config;
}
public boolean getSigningConfig() {
return this.signingConfig;
}
}
该类要注意几个地方:
(1) BuildTypes若为内部类,一定要为静态类,否则会抛出下面的异常:
A problem occurred evaluating project ':app'.
> Could not create an instance of type com.wj.plugin.extension.AndroidExtension$BuildTypes.
> Class AndroidExtension.BuildTypes is a non-static inner class.
(2)BuildTypes一定含有一个参数为String类型的构造函数,否则会抛出下面的异常:
A problem occurred evaluating project ':app'.
> Could not create an instance of type com.wj.plugin.extension.AndroidExtension$BuildTypes.
> Too many parameters provided for constructor for type AndroidExtension.BuildTypes. Expected 0, received 1.
因为在build.gradle文件进行配置该属性的时候,自定义的名字就是传入该构造函数对应的String类型的参数。
(3)一定要有一个name的属性,否则会抛出下面的异常
A problem occurred evaluating project ':app'.
> Unable to determine the name of 'com.wj.plugin.extension.AndroidExtension$BuildTypes@130b3f7d' because it does not have a 'name' property
- 2.将该扩展属性定义在AndroidExtension为一个属性值
public class AndroidExtension {
.......
private NamedDomainObjectContainer<BuildTypes> buildTypes;
public AndroidExtension(Project project) {
buildTypes = project.container(BuildTypes.class);
}
public void buildTypes(Action<NamedDomainObjectContainer<BuildTypes>> action) {
action.execute(buildTypes);
}
public NamedDomainObjectContainer<BuildTypes> getBuildTypes() {
return buildTypes;
}
}
当然也可以直接将该类作为一个单独的属性扩展。因为该实例是紧接着第一部分的内容,所以直接作为androidExtension {}的一个扩展属性。
- 3.将该属性扩展添加到Project中
因为AndroidExtension需要将Project传入到构造函数中,所以在 通过project.getExtensions().create()来创建该Extension的时候,将project传入,代码如下:
void createAndroidExtensions(Project project) {
project.getExtensions().create(AndroidExtension.TAG, AndroidExtension, project)
}
- 4.在build.gradle添加配置属性
//多层闭包的属性扩展
androidExtension {
........
buildTypes {
dev {
signingConfig = true
}
}
}
这里要注意一个问题,再去给signingConfig赋值的时候,不能像Android Gradle中通过下面的方式进行赋值:
signingConfig true
否则会抛出以下异常:
A problem occurred evaluating project ':app'.
> No signature of method: build_dwrb2icq269nomqcc24dytwa1.androidExtension() is applicable for argument types: (build_dwrb2icq269nomqcc24dytwa1$_run_closure7) values: [build_dwrb2icq269nomqcc24dytwa1$_run_closure7@4ddf3c7]
遗留问题:具体原因暂时不清楚
通过Android Studio运行项目,看到对应的buildTypes的相应的信息输出如下:
%%%%%%%%% FirstPluginProject %%%%%%%%% compileSdkVersion = 1.0.0
%%%%%%%%% FirstPluginProject %%%%%%%%% applicationId = 1.0.0
%%%%%%%%% FirstPluginProject %%%%%%%%% minSdkVersion = null
%%%%%%%%% FirstPluginProject %%%%%%%%% buildTypes = dev , signingConfig = true
三 总结
经过了前面几篇的积累,发现现在在看这部分内容的时候游刃有余:
- 1.在定义内层属性的时候,可以通过Action<T>或者Closure的方式对内层属性进行赋值;
- 2.通过NamedDomainObjectContainer来可设置可以自定义名字的属性;但是必须注意有三点:
- (1)若该内层属性类为内部类,则该内部类必须为静态类;
- (2)该内层属性类必须含有一个String类型的构造函数;
- (3)该内层属性类必须有一个name属性
继续加油!!