添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

gradle系列——常用技巧(五)

什么是gradle插件

插件的概念可以说无处不在,例如chrome浏览器就有很多扩展插件,用来扩充网页的某种特定的功能,那gradle里面插件是用来干嘛的呢?

其实也一样,用来扩充某种特定的构建能力。gradle是一个构建框架,一定要注意他是一个框架,框架的作用就是定制规则,定制通用的模块,包括如何去管理项目的子模块,控制Task执行的生命周期等,但具体的构建任务,这个就比较多元化了,gradle不仅仅是给Android用的,java项目,web项目也都可以使用gradle去构建,所以针对不同的项目类型,不同的构建任务,gradle提供了插件这一接口,不同的构建任务对应成一个插件,在你需要的时候自己去引入,例如需要构建一个Apk,就引入Android的gradle插件

插件的使用

使用已有的插件主要有4个步骤

  • 在项目build.gradle中引入插件的仓库,目的是告诉gradle,该项目的插件或者其他三方库可以去哪些仓库里面找
  • repositories {
            // google这些都是gradle内部已经集成的,所以直接像调用方法一样引入
            google()
            mavenCentral()
    
  • 声明导入具体的插件库,一个插件库中可能有多个插件
  • dependencies {
            // 导入Android构建相关的插件库
            classpath "com.android.tools.build:gradle:4.2.2"
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    
  • 在模块module中引入具体的插件
  • // 引入该插件,会引入打包Apk的一系列Task
    apply plugin: 'com.android.application'
    

    插件添加到模块module之后,一般会在当前project下添加两种能力,一种是用于配置的属性,一种是添加不同的Task

  • 配置插件的属性,这个最常见的就是android闭包了,android闭包就是com.android.application这个插件提供的可配置的属性
  • android {
        compileSdkVersion 30
        buildToolsVersion "30.0.3"
        defaultConfig {
            applicationId "com.example.myapplication"
            minSdkVersion 19
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    

    插件属性拓展——gradle DSL

    上面android闭包这种代码看起来跟常规的语言不太一样,像是一种配置方式,但其实就是利用了闭包+delegate的方式实现的DSL,我们可以简单实现一个类似的

    class Android {
        private int mCompileSdkVersion
        private String mBuildToolsVersion
        private DefaultConfig mDefaultConfig = new DefaultConfig()
        def compileSdkVersion(sdkVersion){
            mCompileSdkVersion = sdkVersion
        def buildToolsVersion(buildVersion){
            mBuildToolsVersion = buildVersion
        def defaultConfig(Closure closure){
            closure.delegate = mDefaultConfig
            closure.call()
    class DefaultConfig {
        private String mApplicationId
        private int mMinSdkVersion
        private int mTargetSdkVersion
        def applicationId(appId){
            mApplicationId = appId
        def minSdkVersion(minVersion){
            mMinSdkVersion = minVersion
        def targetSdkVersion(targetVersion){
            mTargetSdkVersion = targetVersion
    def android(Closure closure){
        Android android = new Android()
        // 给闭包设置delegate
        closure.delegate = android
        closure.call()
    // 调用android函数,就类似上面官方插件提供的android闭包的写法了
    android {
        compileSdkVersion 30
        buildToolsVersion "30.0.1"
        defaultConfig {
            applicationId "com.dsl.android"
            minSdkVersion 20
            targetSdkVersion 30
    

    自定义插件

    我们自己也可以通过实现gradle提供的Plugin接口来实现自定义的插件

    主要有3种创建插件的位置

  • 直接在gradle脚本中
  • 在buildSrc目录下
  • 创建一个单独的工程
  • gradle脚本实现

    这种方式很简单,例如在moudule的build.gradle文件中写一个插件

    class MyPlugin implements Plugin<Project>{
        @Override
        void apply(Project project) {
            println "MyPlugin hello"
    // 然后可以直接在文件头部apply这个插件,在配置阶段执行到这行代码的时候实际上就会去执行上面重写的apply方法
    apply plugin:MyPlugin
    

    这种方式目前没找到实际的用处,意义不太大,了解即可

    用buildSrc模块实现

    当插件只在当前项目下使用时,可以使用这种方式

    自定义插件

  • 首先新建一个名为buildSrc的module(gradle会默认识别名为buildSrc的目录为项目的插件目录
  • src目录下仅保留一个空的main文件夹,其他的全部删除,并在main文件夹下新建groovy文件夹和resources文件夹
  • 在groovy文件夹下创建自定义的一个包名文件夹,例如com.tu.gradle.plugin,注意这个是创建了4层文件夹,然后在plugin文件下新建一个groovy文件CustomGradlePlugin.groovy,实现Plugin接口,并实现它的apply方法
  • class CustomGradlePlugin implements Plugin<Project>{
        @Override
        void apply(Project project) {
            println "apply hello CustomGradlePlugin"
    
  • 在resources目录下创建文件夹META-INF.gradle-plugins,注意是两层文件夹,然后在gradle-plugins文件夹下创建一个com.tu.gradle.plugin.properties文件,properties前的文件名部分跟前面创建的包名需要一致,该文件的作用是去指定插件的class
  • implementation-class=com.tu.gradle.plugin.CustomGradlePlugin
    
  • 最后就可以在项目其他moudle中引入使用这个插件了,sync工程之后就能看到在配置阶段执行了自定义插件里apply函数里的代码
  • apply plugin: 'com.tu.gradle.plugin'
    // 输出结果
    > Configure project :app
    apply hello CustomGradlePlugin
    

    如果一个插件模块想写多个插件,是通过包名去区分的,不同的插件只需要放在不同的包下面

    buildSrc的目录结构如图

    增加配置属性

    上面说了插件的主要功能是向当前project提供了配置属性和task,先自定义一个配置属性

    在同一个包目录下新建一个VersionExtension.groovy文件,在里面定义一个实体类,这个实体类里面的属性就是向外提供的可配置属性

    class VersionExtension {
        String mVersionName
        int mVersionCode
        def versionName(name) {
            mVersionName = name
        def versionCode(code) {
            mVersionCode = code
    

    然后在我们的CustomGradlePlugin得apply方法中,把这个扩展配置属性添加到当前project当中

    class CustomGradlePlugin implements Plugin<Project>{
        @Override
        void apply(Project project) {
            println "apply hello CustomGradlePlugin"
            // versionInfo是外部可使用的配置闭包名
            project.extensions.create("versionInfo",VersionExtension.class)
    

    这样就可以在引入该插件得模块脚本中去配置使用

    versionInfo {
        versionName "1.0.0"
        versionCode 100
    

    自定义Task

    配置属性的最终目的是需要在插件的Task中去使用,下面在插件中自定义一个Task

    同样在一个包目录下新建一个CustomPluginTask.groovy文件

    class CustomPluginTask extends DefaultTask {
        CustomPluginTask(){
            // 指定Task得分组
            group = "custom_plugin"
        @TaskAction
        void doAction(){
            // 使用配置参数
            def versionName = project.extensions.versionInfo.mVersionName
            def versionCode = project.extensions.versionInfo.mVersionCode
            println "custom plugin versionName is $versionName and versionCode is $versionCode"
    

    然后在CustomGradlePlugin的Apply方法里,把创建的这个task添加到当前project中

    class CustomGradlePlugin implements Plugin<Project>{
        @Override
        void apply(Project project) {
            println "apply hello CustomGradlePlugin"
            project.extensions.create("versionInfo",VersionExtension.class)
            // customPluginTask是该Task的名称
            project.tasks.create("customPluginTask",CustomPluginTask.class)
    

    最后同步一下,当前module下就能看到这个customPluginTask

    执行一下看看

    > Task :app:customPluginTask
    custom plugin versionName is 1.0.0 and versionCode is 100
    

    到这,一个简单得自定义gradle插件就完成了,是不是很简单

    有的博客说这种方式还要改builsSrc目录下的build.gradle文件,实践发现,这种方式根本就不需要build.gradle文件,我使用的gradle 6.7.1

    使用这种方式主要就是供本项目使用,如果想要发布供其他项目使用的话,buildSrc是有一个坑点的,就是buildSrc模块属于是项目保留得一个模块,如果在setting.gradle中去include这个模块得话,就会报错

    'buildSrc' cannot be used as a project name as it is a reserved name
    

    但如果想要把插件发布得maven仓库,就必须把这个插件module include到setting.gradle脚本中,这样才能正常使用uploadArchives的task上传maven,所以使用这种方式的应用场景就是只在本项目直接使用(我实验是这样得,但看有得blog说可以,不知道是不是我操作有问题或者是gradle版本问题)

    单独的gradle插件工程

    这种方式跟在buildSrc模块的方式差不多,主要的区别在于插件module的build.gradle文件,需要去配置依赖的库,源码路径等信息,而且插件module可以任意取名(不能取名buildSrc),实际应用中推荐使用这种方式去做,可操作性比较强

    apply plugin: 'groovy'
    repositories {
        mavenCentral()
    dependencies {
        implementation localGroovy()
        implementation gradleApi()
    sourceSets {
        main{
            groovy {
                srcDir 'src/main/groovy'
            resources {
                srcDir 'src/main/resources'
    

    把写的自定义插件发布成一个独立的库,这样可方便其他模块使用,发布可以分成两种,本地和远程

    发布插件到本地

    很简单,利用maven插件,在插件module的build.gradle中引入maven插件,并配置uploadArchives闭包

    apply plugin: 'groovy'
    apply plugin: 'maven'
    repositories {
        mavenCentral()
    dependencies {
        implementation localGroovy()
        implementation gradleApi()
    sourceSets {
        main{
            groovy {
                srcDir 'src/main/groovy'
            resources {
                srcDir 'src/main/resources'
    uploadArchives {
        repositories.mavenDeployer {
            repository(url: uri('../repo'))   // 本地仓库路径
            pom.groupId = "com.tu.gradle.plugin"// group唯一标识,可任意,通常为模块包名
            pom.artifactId = "CustomGradlePlugin"// 插件项目名称,通常为插件库模块名称,可以任意,一个插件库中不同的插件通过不同的包名去区分
            pom.version = "0.0.1"// 版本号
    

    配置之后,sync下项目,就能在gradle视图的插件module中看到uploadArchives的task,运行这个task就能把插件发布到本地

    发布插件到远程

    跟发布插件到本地的差距就是配置相应的远程maven url及用户名密码

    uploadArchives {
        repositories.mavenDeployer {
            repository(url: REMOTE_MAVEN_URL) {                 authentication(userName: "tu", password: "123456")             
            pom.groupId = "com.tu.gradle.plugin"// group唯一标识,可任意,通常为模块包名
            pom.artifactId = "CustomGradlePlugin"// 插件项目名称,通常为插件库模块名称,可以任意,一个插件库中不同的插件通过不同的包名去区分
            pom.version = "1.0.0"// 版本号
    

    使用发布的插件

    发布之后,就可以像平时使用三方插件一样去使用

    先在根项目下声明引入插件

    repositories {
            // 本地maven相对地址
            maven {
                url 'repo'
    dependencies {
            classpath "com.android.tools.build:gradle:4.2.2"
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
            classpath "com.tu.gradle.plugin:CustomGradlePlugin:1.0.0"
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
    

    然后在子module如app模块下使用

    apply plugin: 'com.tu.gradle.plugin'
    

    以上就是gradle插件相关知识的分享,如果有写的不对的地方欢迎批评指正,感觉写的还不错的也欢迎评论点赞支持一下

    分类:
    Android
    标签: