AndroidPluginStudy

Introduction: 实现自定义 Plugin,使用 ASM 可以对 class、aar、jar 的代码进行修改,自定义 plugin 配置等。
More: Author   ReportBugs   
Tags:

ASM 是一个通用的Java字节码操作和分析框架。它可以直接以二进制形式用于修改现有类或动态生成类。 ASM提供了一些常见的字节码转换和分析算法,可以从中构建定制的复杂转换和代码分析工具。 ASM提供与其他Java 字节码框架类似的功能,但侧重于性能。因为它的设计和实现是尽可能的小和尽可能快,所以它非常适合在动态系统中使用(但当然也可以以静态方式使用,例如在编译器中)。

自定义 plugin 开发

Gradle从 1.5 开始,Gradle插件包含了一个叫Transform的 API,这个 API 允许第三方插件在class文件转为为dex文件前操作编译好的class文件,这个 API 的目标是简化自定义类操作,而不必处理Task,并且在操作上提供更大的灵活性。并且可以更加灵活地进行操作。官方文档:http://google.github.io/android-gradle-dsl/javadoc/

创建插件

AndroidStudio 中创建一个纯净的Module ,删除 res 文件夹和 AndroidManifest.xml 文件。

Gradle 会默认在配置初始化前,编译 buildSrc 这个项目,可以是 java、groovy、kotlin 项目,并把项目配置到 classpath 下。所以如果Module 命名为 buildSrc 那么不需要在 Project 级别的 build.gradle 文件中使用 classpath 引入,也不需要在 app 级别的 build.gradle 文件中使用 apply plugin: 进行应用。

下面我们介绍的是自定义的 Plugin 插件,是编译之后需要引入项目的。

类继承Plugin

public class AmsPlugin implements Plugin<Project> {
    @Override
    public void apply(@NonNull Project project) {
        AppExtension appExtension = project.getExtensions().findByType(AppExtension.class);
        assert appExtension != null;
        //注册 task 任务
        appExtension.registerTransform(new AmsTransform(project));
    }
}
public class AmsTransform extends Transform{
      /***部分代码省略***/
}

build.gradle 文件配置

配置properties

/src/main/resources/META-INF/gradle-plugins 目录下创建xxx.properties 文件,这个 xxx 就是以后项目build.gradle 文件中需要apply 的插件名称。

implementation-class=com.tzx.ams.plugin.AmsPlugin

com.tzx.ams.plugin.AmsPlugin 就是插件对应的类。

properties

生成插件

我们在执行 uploadArchives 的任务的时候就在我们对于的仓库生成了我们需要的插件。

在项目中引入插件

项目的根 build.gradle配置

buildscript {
    repositories {
        maven {//添加 repo 本地仓库
            url uri("repo")
        }
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        //添加插件依赖
        classpath 'com.tzx.ams:ams-plugin:1.0.0'
    }
}

项目的 build.gradle 配置

apply plugin: 'com.android.application'
apply plugin: 'amsplugin'
/***部分代码省略***/

plugin 自定义配置

配置类

public class AmsConfig {
    //日志开关
    public boolean isDebug;
    //class 包含 str 则不处理
    public String[] filterContainsClassStr;
    //class 以 str 开头则不处理
    public String[] filterstartsWithClassStr;
    //拦截在这个文件中声明的 class
    public String filterClassFile;
    public List<String> filterClassNameList;
    //需要进行注入的 method
    public String amsMethodFile;
    //需要进行注入的 method 对应的 tag
    public String amsMethodTag;
    public List<Pair<String, String>> amsMethodFileList;
}

创建AmsConfig 的类对象

public class AmsPlugin implements Plugin<Project> {
    @Override
    public void apply(@NonNull Project project) {
        AppExtension appExtension = project.getExtensions().findByType(AppExtension.class);
        assert appExtension != null;
        //注册优先于 task 任务的添加
        project.getExtensions().create("AmsConfig", AmsConfig.class);
        appExtension.registerTransform(new AmsTransform(project));
    }
}

build.gradle 文件中定义配置(如果 gradle 中没有定义,那么会反射构造一个对象)

apply plugin: 'com.android.application'
apply plugin: 'amsplugin'
AmsConfig{
    isDebug = true
    filterContainsClassStr = ["R.class", ".R\$"]
    filterstartsWithClassStr = ["android"]
    filterClassFile = "amsfilterclass.text"//build.gradle 相同目录级别的文件名
    amsMethodFile = "amsmethods.text"//build.gradle 相同目录级别的文件名
    amsMethodTag = "TEST"
}

获取gradle 中定义的配置

AmsConfig amsConfig = (AmsConfig) this.project.getExtensions().getByName(AmsConfig.class.getSimpleName());
//配置文件的获取
String projectDri = this.project.getProjectDir().getAbsolutePath()
String fileName = projectDri + File.separatorChar + amsConfig.filterClassFile;

这个 amsConfig 在使用的时候不用判空,如果调用project.getExtensions().create 添加了那么就会反射构造出这个对象。如果gradle 文件中定义了,那么这个对象就会被赋与相应的属性值。

如果在获取的时候,没有被create那么就会跑出一下异常:

* Where:
Build file '/Users/tanzx/AndroidStudioWorkSpace/GitHub/AmsDemo/app/build.gradle' line: 3
* What went wrong:
A problem occurred evaluating project ':app'.
> Could not find method AmsConfig() for arguments [build_5n6idkxwtmzfflm5k30ynjblo$_run_closure1@2cf1f355] on project ':app' of type org.gradle.api.Project

日志

./gradlew build 的日志

> Task :app:transformClassesWithAmsTransformForRelease
AmsTransform filterClassName:com.tzx.amsdemo.App.class
AmsTransform filterClassName:com.tzx.amsdemo.BuildConfig.class
AmsTransform amsMethodFile:maintest#com.tzx.amsdemo.MainActivity.class
AmsTransform Find jar input: android.local.jars:AmsTestJar_V1.0.jar:2069720caf021a802230197880600b9eb8ea02c8
AmsTransform Modifyjar: com/tzx/ams_test_jar/AmsTestJar.class
AmsMethodVisitor visitAnnotation Lcom/tzx/ams/TrackMethod;
AmsMethodVisitor TEST  methodName=test
AmsTransform Modifyjar: com/tzx/ams_test_jar/BuildConfig.class
AmsTransform Find jar input: androidx.appcompat:appcompat:1.1.0
AmsTransform Find jar input: androidx.constraintlayout:constraintlayout:1.1.3
AmsTransform Find jar input: androidx.fragment:fragment:1.1.0
AmsTransform Find jar input: androidx.appcompat:appcompat-resources:1.1.0
AmsTransform Find jar input: androidx.drawerlayout:drawerlayout:1.0.0
AmsTransform Find jar input: androidx.viewpager:viewpager:1.0.0
AmsTransform Find jar input: androidx.loader:loader:1.0.0
AmsTransform Find jar input: androidx.activity:activity:1.0.0
AmsTransform Find jar input: androidx.vectordrawable:vectordrawable-animated:1.1.0
AmsTransform Find jar input: androidx.vectordrawable:vectordrawable:1.1.0
AmsTransform Find jar input: androidx.customview:customview:1.0.0
AmsTransform Find jar input: androidx.core:core:1.1.0
AmsTransform Find jar input: androidx.cursoradapter:cursoradapter:1.0.0
AmsTransform Find jar input: androidx.versionedparcelable:versionedparcelable:1.1.0
AmsTransform Find jar input: androidx.collection:collection:1.1.0
AmsTransform Find jar input: androidx.lifecycle:lifecycle-runtime:2.1.0
AmsTransform Find jar input: androidx.lifecycle:lifecycle-viewmodel:2.1.0
AmsTransform Find jar input: androidx.savedstate:savedstate:1.0.0
AmsTransform Find jar input: androidx.interpolator:interpolator:1.0.0
AmsTransform Find jar input: androidx.lifecycle:lifecycle-livedata:2.0.0
AmsTransform Find jar input: androidx.lifecycle:lifecycle-livedata-core:2.0.0
AmsTransform Find jar input: androidx.arch.core:core-runtime:2.0.0
AmsTransform Find jar input: androidx.arch.core:core-common:2.1.0
AmsTransform Find jar input: androidx.lifecycle:lifecycle-common:2.1.0
AmsTransform Find jar input: androidx.annotation:annotation:1.1.0
AmsTransform Find jar input: androidx.constraintlayout:constraintlayout-solver:1.1.3
AmsTransform Find jar input: :ams
AmsTransform Modifyjar: com/tzx/ams/BuildConfig.class
AmsTransform Modifyjar: com/tzx/ams/MethodEventManager.class
AmsTransform Modifyjar: com/tzx/ams/MethodObserver.class
AmsTransform Modifyjar: com/tzx/ams/TrackMethod.class
AmsTransform Find dir input:classes
AmsTransform Modifydir: com.tzx.amsdemo.MainActivity.class
AmsClassVisitor visitMethod: com/tzx/amsdemo/MainActivity TEST methodName= maintest
AmsTransform Modifydir: com.tzx.amsdemo.TimeObserver.class
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools