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

Posted 好人静

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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属性

        继续加油!!

以上是关于Android 自定义Gradle插件的多层属性扩展的主要内容,如果未能解决你的问题,请参考以下文章

Android Gradle中的productFlavors

Android Gradle 插件Gradle 自定义 Plugin 插件 ④ ( 为自定义 Gradle 插件的扩展配置扩展 | 在自定义插件中获取扩展属性 )

Android Gradle 插件Gradle 扩展属性 ① ( Gradle 扩展属性简介 | Gradle 自定义 task 任务示例 )

Android Gradle 插件Gradle 扩展属性 ① ( Gradle 扩展属性简介 | Gradle 自定义 task 任务示例 )

自定义gradle插件

Android Gradle 插件自定义 Gradle 任务 ⑬ ( DefaultTask 中的任务输入和输出属性 | TaskInputs 任务输入接口 | FileCollection )