Android 自定义Gradle插件,使用Transform

1,747 阅读4分钟

Android 自定义Gradle插件

简介、

首先新创建一个Android 项目,刚开始只有一个Android-app的application模块,其他的你先不要看,这整个项目模块结构是为了完成一个arouter项目,因为这是为了关注自定义Gradle插件,所以下面重点讲解ArouterPlugin这个Java模块的创建使用流程。

Arouter整体结果如下:

image.png

一、创建插件模块

1.1、new-》new module->选择java模块创建

image.png

image.png

1.2、创建好模块后

步骤:

  1. 首先将mian目录下所有东西删除
  2. 再创建一个groovy模块文件夹,按照下图创建好目录文件夹,内容后面再说
  3. 再创建一个resources文件夹,文件名称 META-INF/gradle-plugins/com.example.pluginarouter.properties,必须按照这个结构创建文件, 注意:com.example.pluginarouter 这个名称后续会用到

image.png

1.3、配置插件模块的build.gradle文件,创建插件仓库用于生成jar包,便于其他模块可以使用插件

下面的配置,注意 gradle版本为4.1.0,本来想用最新的gradle版本7.0.2但是,因为太新了,废弃了Transform(借助它编译时扫描Android所有的class文件,完成字节码插桩) Api,现在应该怎么做,官方文档也没说清楚,所以我暂时使用4.1.0 ,并且官网

gradle插件版本为7.0.1,插件版本7.0以上需要使用publishing的方式配置生成

apply plugin: 'groovy'


dependencies {
    //gradle sdk
    implementation gradleApi()
    //groovy sdk
    implementation localGroovy()

//    implementation "com.android.tools.build:gradle:7.0.1"
    implementation "com.android.tools.build:gradle:4.1.0"
}

//以下内容主要用于发布插件
//gradle 7.0之后过时  参考:https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:complete_example
//apply plugin: 'maven'
//repositories {
//    mavenCentral()
//}
//group = 'com.example.myplugin'
//version = '1.0.3'
//
//uploadArchives {
//    repositories {
//        mavenDeployer {
////            ./是当前目录../是父级目录
//            repository(url: uri('./../repo'))
//        }
//    }
//}


//以下内容主要用于发布插件---gradle插件版本7.0以上
apply plugin:  'maven-publish'
repositories {
    mavenCentral()
}
group = 'com.example.arouterplugin'
version = '1.0.0'

publishing {
    publications {
        mavenJava(MavenPublication) {
            groupId = group
            artifactId = 'arouter'
            version = version
            from components.java

//            versionMapping {
//                usage('java-api') {
//                    fromResolutionOf('runtimeClasspath')
//                }
//                usage('java-runtime') {
//                    fromResolutionResult()
//                }
//            }
//            pom {
//                name = 'My Library'
//                description = 'A concise description of my library'
//                url = 'http://www.example.com/library'
//                properties = [
//                        myProp: "value",
//                        "prop.with.dots": "anotherValue"
//                ]
//                licenses {
//                    license {
//                        name = 'The Apache License, Version 2.0'
//                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
//                    }
//                }
//                developers {
//                    developer {
//                        id = 'johnd'
//                        name = 'John Doe'
//                        email = 'john.doe@example.com'
//                    }
//                }
//                scm {
//                    connection = 'scm:git:git://example.com/my-library.git'
//                    developerConnection = 'scm:git:ssh://example.com/my-library.git'
//                    url = 'http://example.com/my-library/'
//                }
//            }
        }
    }
    repositories {
        maven {
            // change URLs to point to your repos, e.g. http://my.org/repo
//            def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
//            def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
//            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
//            ./是当前目录../是父级目录
            url ='./../repo'
        }
    }
}

1.4、使用自定义的插件打印一句话这是最简单的

按照上图在groovy模块文件夹下,创建一个MyPlugin的java文件

package com.example.arouterplugin;

import com.android.build.gradle.AppExtension;

import org.gradle.api.Plugin;
import org.gradle.api.Project;



/**
 * @author tgw
 * @date 2021/10/16
 * @describe
 *
 * //Asm的使用:https://www.jianshu.com/p/905be2a9a700
 * 使用asm提供的通过 ASMifier 自动生成对应的 ASM 代码。首先需要在ASM官网 下载 asm-all.jar 库,我下载的是最新的 asm-all-5.2.jar,然后使用如下命令,即可生成
 *  命令:
 * java -classpath E:\googleDowmload\asm-all-5.1.jar org.objectweb.asm.util.ASMifier E:\MyLearing\MyAptUseLearing\base-arouter\build\tmp\kotlin-classes\debug\com\example\base_arouter\ARouterUtils.class
 *
 * jar包下载地址:
 * http://nexus.neeveresearch.com/nexus/content/repositories/public/org/ow2/asm/asm-all/5.1/
 */
class MyPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
            AppExtension android = (AppExtension) project.getExtensions().getByType(AppExtension.class);
            android.registerTransform(new ScanAllFileTransform(project));
            System.out.println("MyPlugin自定义独立插件0.................");
    }
}

image.png

上面的ScanAllFileTransform文件暂时不要管,这是为了做编译时扫描文件所用的,我们这只是为了打印一句话

1.5、配置插件文件在前面创建的插件配置文件com.example.pluginarouter.properties下:

内容如下

implementation-class = com.example.arouterplugin.MyPlugin

image.png

1.6、生成插件的jar包:

选择你的插件模块点击publish生成jar,jar包的生成路径就是你在1.3中配置的路径

image.png

仓库jar生成如下 image.png

1.7、使用插件jar包

1.7.1、在你想要使用插件的模块下配置,插件仓库路径,如app模块下

注意仓库地址要在最下面:如下图

image.png

1.7.2、使用插件,插件的名称就是,你前面创建的插件配置文件的文件名称

image.png

//引入插件ArouterPluginmodule下的MyPlugin,
// 不能放在上面的plugins中,
apply plugin: 'com.example.pluginarouter'

1.8、结果:

这时候你clear 项目 再次编辑就,能够在编译时看到你写的插件内容:

image.png

image.png

结语:我们使用插件肯定不是为了打印一句话

二、我们使用gradle中的Transform这个类去扫描class文件,收集自己想要的去进行字节码插桩

具体的大家可以参考阿里的Arouter组件大体用法差不多

大致思路如下:

遍历所有的class文件,找出你想要插桩的文件存入集合(这里存在规则匹配,具体的看你自己),利用Asm进行字节码插桩

篇幅过长下次再说: