深入Gradle插件开发

上面一篇文章学会了基本的Gradle插件开发流程,相当于Helloworld,这次深入的探究下Gradle插件开发的其他方面,一个新建的app工程的build.gradle通常有如下内容:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "org.cmdmac.aoptest"
        minSdkVersion 22
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
}

刚入门的时候或许会有很多资料告诉你每个配置是什么意思,但是为什么可以这么写,大括号是什么意思,有哪些属性就不清楚了。

这就涉及gradle语法其实也就是groovy语法还有gradle插件的知识,这里先做简单的介绍,网上也有很多的资料,首先每个build.gradle文件就是一个groovy脚本,gradle支持dsl语法也就是可以大括号包起来一行一行的配置属性,其实也可以使用像java一样调用函数的形式来实现(groovy本身就是基于jvm的语言,直接写Java代码也是可以的),下面可以一行一行的解析每句话的意思

//应用com.android.application插件,调用这句类似于import了一个js文件也会调用插件内部实现的apply方法,执行后就可以使用这个插件的变量和函数
apply plugin: 'com.android.application'
//每个gradle文件其实有个内建变量叫project,这有点像是浏览器js里的window变量,android其实也是个变量,project.android跟android {xxx}是等价的
android {
    //android变量有个compileSdkVersion属性,这一行android.setCompileSdkVersion(27)是等价的,每行的属性设置其实都有对应的函数实现
    compileSdkVersion 
    //大括号包起来的叫闭包,里面相当是一个属性集合
    defaultConfig {
        applicationId "org.cmdmac.aoptest"
        minSdkVersion 22
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
    }
    //...
    }
}
可以在~/.gradle/caches/modules-2/files-2.1/com.android.tools.build/gradle/下面找到对应插件的实现源码,等你学会了插件开发你能看懂里面具体是怎么做到的了。

本文并不是讲解每个属性和语法的含义的,本文是讲解如何在插件里实现自己的属性的

1.属性扩展

属性扩展就是要做到可以直接在gradle文件里使用的变量,比如google的插件里的android这个属性变量,具体是怎么做的呢?

首先实现一个扩展类

//扩展类HugoExtension.groovy
class HugoExtension {
  def message = 'Hugo'
  def setTest(String str) {
    System.out.println("Hugo:" + str);
  }
}

然后在插件实现里注册一个扩展

class HugoPlugin implements Plugin {
  @Override void apply(Project project) {
	//注册扩展,hugo是在build.gralde文件里使用的对象名,HugoExtension扩展类类名
	project.extensions.create('hugo', HugoExtension)
  }
}

这样就实现了一个扩展,使用方法:

//build.gradle中可以这样使用
hugo.message = 'hello world'
//或者
hugo {
    message 'hello world'
    //也可以
    message = 'hello world'
    //会调用setTest('haha')
    test 'haha'
    //会调用setTest('vava')
    setTest 'vava'
    //不可以test = 'haha'或者setTest = 'vava',因为他们不是属性变量,是个函数
}

从上面的例子你就可以理解android插件里面是怎么个原理了

2.多个属性

//对应注册的类ListExtention.groovy
class ListExtention {
  String name
  String msg
/*
需要string的构造函数,否则报错:
A problem occurred evaluating project ':aop'.
> Could not create an instance of type org.cmdmac.aopplugin.ListExtention.
   > Could not find any public constructor for class org.cmdmac.aopplugin.ListExtention which accepts parameters [java.lang.String].
*/
  public ListExtention(String n) {
    this.name = n
  }
}
//HugoPlugin.groovy
class HugoPlugin implements Plugin {
  @Override void apply(Project project) {
	//在插件中注册
    def lists = project.container(ListExtention)
    //lists是在build.gradle中使用的名称
    project.extensions.lists = lists
  }
}

这种多个属性可以在build.gradle中这样使用

lists {
    hello {
/*需要带等号,否则出错:> Could not find method msg() for arguments [m222] on object of type org.cmdmac.aopplugin.ListExtention.*/
        name = 'abc'
        msg = 'm111'
    }
    //中间写xxx 'aa'会报错,说找不到方法什么的
    hello1 {
        name = 'def'
        msg = 'm222'
    }
}
//如果想不用等号需要增加同时的函数,但不是list时可不定义函数也可以实现
 def msg(String n){
   this.msg = n;
 }

3.属性嵌套

这里要讲的就是像android插件这种了:

android {
    compileSdkVersion 
    defaultConfig {
        applicationId "org.cmdmac.aoptest"
    }
}
这个看起来跟多个属性好像是一样的,但其实是不一样的,不同点在于,上面那种是每个大括号里面的属性是一样的,hello和hell1是并列的关系,这一层中也不能加入其他属性如果要支持加入就得使用现在讲的属性嵌套扩展。

那这种是如何实现的呢,假设要想在build.gradle中使用:

Options {
    xxx = 'yyy'
    options {
        xxx = 'lll'
    }
}

那么要先实现扩展类

//OptionsExtentions.groovy
class OptionsExtentions {
    def String xxx;
    def String yyy;
    def Options options;
    //@javax.inject.Inject
    //需要使用ObjectFactory构造
    public OptionsExtentions(ObjectFactory objectFactory) {
        options = objectFactory.newInstance(Options)
    }
    //使用时的名称,也表明这是一个闭包属性
    void options(Action action) {
        action.execute(options)
    }
}
//嵌套类实现Options.groovy
class Options {
    def String xxx = 'def'
}
//HugoPlugin.groovy
class HugoPlugin implements Plugin {
    @Override void apply(Project project) {
    //在插件中先注册,Options名称是使用时的名称
    project.extensions.create('Options', OptionsExtentions, project.objects)
}
这样就实现了Options里面再使用options这个属性,需要注意的是外层的xxx和里面的xxx是不一样的,因为各自实现在不同的扩展类中。
以上就是三种扩展属性的方法,掌握这三种方法后相信可以完全定制自己想要的dsl格式配置。

总结 

gradle插件开发其实并不难,其实最主要的基础还是groovy语言和gradle本身的规则,比如什么是project、task以及task的依赖关系等。
demo在github中可以找到  AOPAndroid

你可能感兴趣的:(gradle,android,移动开发,android)