Android Gradle学习(四)- gradle插件开发和调试

690 阅读4分钟

AOP思想

AOPAspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Android中常用的AOP方式有四种:

  • APT : (Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。
  • KAPT : (Kotlin Annotation Processing Tool) ,即用于kotlin的apt,将kotlin代码翻译成java,再由APT处理
  • KSP : (Kotlin Symbol Processing),Kapt的升级版,可识别kotlin代码直接处理,比KAPT
  • gradle plugin : gradle插件,功能更强大,不仅可以生成代码,还可以修改源文件字节码、资源文件等,还可以hook打包流程,处理额外的操作等

gradle plugin 7.0之前,使用Transform,本篇基于4.1.3改造。gradle plugin 7.0开始,Transform废弃,这个方案后面再说。

1. 自定义gradle plugin

1.1 创建插件module

File -> New -> Module -> Java or Kotlin Library,右侧Build Configuration Language选择Groovy DSL (build.gradle)即可,表示使用groovy语言。如果对kotlin熟悉的,选择另一个Kotlin DSL (build.gradle.kts)也可。

1.2 build.gradle引入项目依赖

plugins {  
    id 'groovy' // Groovy Language  
    id 'java-gradle-plugin' // Java Gradle Plugin  
}  
  
repositories {  
    mavenCentral()  
}  
  
dependencies {  
    implementation gradleApi()  
    implementation localGroovy()  
}  

1.3 创建插件入口处理类

commonplugin
|_src
    |_main
        |_groovy
            |_com
                |_kongge
                    |_commonplugin
                        |_CommonPlugin.groovy
package com.kongge.commonplugin  
  
import org.gradle.api.Plugin  
import org.gradle.api.Project  
  
class CommonPlugin implements Plugin<Project> {  
  
    @Override  
    void apply(Project project) {  
        println '----------CommonPlugin start----------'  

        println '----------CommonPlugin end----------'  
    }  
}

1.4 配置插件

build.gradle下方配置插件id和主入口class

dependencies {
    // ...
}

gradlePlugin {  
    plugins {  
        modularPlugin {  
            // Plugin id.  
            id = 'com.kongge.common_plugin'  
            // Plugin implementation.  
            implementationClass = 'com.kongge.commonplugin.CommonPlugin'  
        }  
    }  
}

AS Build -> Make Module 'xx.commonplugin'构建,会生成该插件的配置文件com.kongge.common_plugin.properties

commonplugin
|_build
    |_pluginDescriptors
        |_groovy
            |_com.kongge.common_plugin.properties

com.kongge.common_plugin.properties内容如下:

implementation-class=com.kongge.commonplugin.CommonPlugin

2. 插件发布到本地仓库

build.gradle加入maven插件

plugins {  
    id 'groovy' // Groovy Language  
    id 'java-gradle-plugin' // Java Gradle Plugin  
    id 'maven'  // 加入插件
}

// ...

uploadArchives {  
    repositories {  
        mavenDeployer {  
            pom.groupId = 'com.kongge'  
            pom.artifactId = 'common-plugin'  
            pom.version = '1.0.0'  
            repository(url: uri('../repo'))  // 表示当前module根目录的父目录
        }  
    }  
}

gradle同步之后,在Gradle面板会看到发布任务uploadArchives

image.png

如果没找到这个任务,在AS设置里面勾选Configure all Gradle tasks during Sync

image.png

发布成功后,可以在项目根目录找到repo目录,即发布成功

image.png

3. 集成使用插件

项目根目录的build.gradle中引入依赖

buildscript {  
    repositories {  
        // ...  
        maven {  
            url uri('./repo') // 注意是一个“.”,表示当前目录下的repo目录
        }  
    }  
    dependencies {  
        // ...
        classpath 'com.kongge:common-plugin:1.0.0'  
    }  
}  
  
allprojects {  
    repositories {  
        // ...  
        maven {  
            url uri('./repo')  
        }
    }  
}

app build.gradle引入插件

apply plugin: 'com.kongge.common_plugin'

构建项目,可以看到插件内的输出,至此自定义的插件流程基本完成。

PS E:\git\Demo2> ./gradlew assembleDebug

> Configure project :app
config.gradle load start
----------CommonPlugin start----------
----------CommonPlugin end----------

4. gradle插件扩展机制

有个需求,希望在APP项目里面动态设置当前插件是否生效和其他参数配置,可以使用扩展机制实现,类似如下配置

commonParam {  
    enable = true  
    doubleClickTimeSpace = 600  
}

首先定义一个参数接收类,其成员变量就是接收的参数,如CommonParam.groovy

package com.kongge.commonplugin  
  
import org.gradle.api.Project  
  
class CommonParam {  

    public static final String EXT_NAME = "commonParam"  

    boolean enable = false  
    long doubleClickTimeSpace = 500  

    static CommonParam parseExt(Project project) {  
        CommonParam commonParam = project.extensions.findByType(CommonParam.class)  
        if (commonParam == null) {  
            commonParam = new CommonParam()  
        }  
        return commonParam  
    }  
}

然后在插件入口解析参数CommonPlugin.groovy

package com.kongge.commonplugin  
  
import org.gradle.api.Plugin  
import org.gradle.api.Project  
  
class CommonPlugin implements Plugin<Project> {  
  
    @Override  
    void apply(Project project) {  

        applyExtension(project)  
        println '----------CommonPlugin start----------'  
        doSomething(project)  
        println '----------CommonPlugin end----------'  
    }  

    private void applyExtension(Project project) {  
        project.extensions.create(CommonParam.EXT_NAME, CommonParam.class)  
    }  

    private void doSomething(Project project) {  
        project.afterEvaluate {  
            CommonParam commonParam = CommonParam.parseExt(project)  
            println "enable=${commonParam.enable}"  
            println "doubleClickTimeSpace=${commonParam.doubleClickTimeSpace}"  
        }
    }  
}

最后发布该插件,然后在APP build.gradle中配置参数

android {
    // ..
}

commonParam {  
    enable = true  
    doubleClickTimeSpace = 600  
}

构建后输出

> Configure project :app
config.gradle load start
----------CommonPlugin start----------
----------CommonPlugin end----------
enable=true
doubleClickTimeSpace=600

要点如下

  1. 定义参数接收class,其成员遍历即接收参数
  2. 在插件入口classapply方法中解析扩展参数
  3. 由于扩展参数解析时机比apply执行时机晚,所以需要在project.afterEvaluate生命周期中再获取扩展参数

5. gradle插件调试

5.1 配置调试参数 app -> Edit Configurations... -> Remote JVM Debug

image.png

image.png

Name随便填,Use module classpath选择要调试插件module,其他默认即可 image.png

5.2 启动调试

打开Gradle面板,找到主项目build任务,右键选择Debug 'Demo2:app [build]'运行 image.png image.png

至此可以断点调试了。可能每个版本的AndroidStudio配置略有差异,遇到不一样的稍微摸索下试试可能就成功了,一般版本越高配置越简单。