Android 自定义Gradle插件的多层属性扩展(五)

前言

         逐步整理的一系列的总结:

        Android Gradle插件开发初次交手(一)

        Android Gradle的基本概念梳理(二)

        Android 自定义Gradle插件的完整流程(三) 

       Android 自定义Task添加到任务队列(四)

        Android 自定义Gradle插件的Extension类(五)

        Android Gradle 中的Transform(六)

        Android Gradle之Java字节码(七)         

       Android Gradle 中的字节码插桩之ASM(八)

      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属性

        继续加油!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值