背景:
1.趋势:目前很多知名框架在后期维护中基于性能优化,代码解耦的考虑都加入Gradle Plugin的技术,如Alibaba的ARouter, 360的Replugin等等。在后期的版本升级后,在性能上都有很大的优化。
2.需求:随着项目中对于APM(Application Performance Management) 越来越关注,后期功能上会加入性能监控,网络请求耗时等一些监控,这个时候为了减少对原业务功能的影响,达到“无需修改原有结构,无侵入接入”和“无性能损害”的目标,就需要有一种办法在编译阶段自动插入代码实现(运行阶段操作肯定会消耗性能,pass)。那就是传说中的“插桩”,AOP思想(面向切面编程),使用Gradle Plugin 技术。
3.现状:产品经理为了对产品上线之后用户操作数据的统计,每次上线前会对代码中增加埋点。随着功能的庞大和业务的复杂,造成埋点代码越来越多,这些代码在某种程度会影响业务逻辑的阅读。这个时候如果能把埋点代码和业务代码进行分离,只要在编译的时候把埋点代码注入到业务代码就可以了。
概述
Gradle Plugin是Android项目中为了达到优化性能,代码解耦的目标,在编译阶段通过字节码的改变在已存在的类中插入代码,或者创建一个新的类,采用的是AOP面向切面思想编程,接下来以下几个方面进行讲述:
1.Android App的编译过程
2.自定义Gradle Plugin的创建和Transform api的使用
3.使用asm实现字节码插桩
编译过程
编译过程
上图介绍了一个app是如何编译成在手机上运行的apk,gradle plugin 是在生成.class文件之后,生成.dex之前对字节码进行操作。它其中包含.class 文件和第三方的jar文件,在编译过程中遍历拿到这些文件,然后对需要更改的类文件进行更改。
自定义Gradle-Plugin 插件
1.自定义插件
1.首先引入jar包:
2.0.0以前:compile'com.android.tools.build:transform-api:1.5.0'
2.0.0以后://从2.0.0版本开始就是在gradle-api中
implementation'com.android.tools.build:gradle-api:3.1.4
2.创建自定义插件:
首先创建自定义插件,在自定以插件中注册一个自定义的transform,编写语言可以是groovy,java,kotlin
Arouter 创建的自定义插件
如上图apply方法中只有在主app中才会需要导入这个插件生成注册的代码。然后创建自定义RegisterTransform 注册到插件中,其中创建了一个集合,赋值给这个插件,这个会在后面讲解。
2自定义transform
1.transform原理及应用
transform原理
1.每一个transform 是一个Gradle task,TaskMandger 会把Transform 串联起来,第一个 Transform 会把javac编译的class文件,jar包,resource(asset中的资源)接收到,然后进行处理,传送给下一个Transform。
2.自定义的transform会插到系统自带的前面。
3.dexBuilder,dexMerger,mergeJavaRes,mergeJniLibs,proguard 这些常见Transform都属于系统的Transform。
2.transform类详解
自定义Transform
1.getName:返回插件的名字,这个 name 并不是最终的名字,
格式如:transformClassesWith“name”ForDebug
2.getInputTypes: 获取过滤的类型:
CONTENT_CLASS:表示要处理编译后的字节码,可能是 jar 包也可能是目录
CONTENT_RESOURCES:表示处理标准的 java 资源,在assert文件夹中
3.getScopes:获取过滤的范围
PROJECT: 只处理当前项目
SUB_PROJECTS:只处理子项目
PROJECT_LOCAL_DEPS:只处理当前项目的本地依赖,例如jar, aar
EXTERNAL_LIBRARIES:只处理外部的依赖库
PROVIDED_ONLY:只处理本地或远程以provided形式引入的依赖库
TESTED_CODE:测试代码
4 isIncremental: 当前 Transform 是否支持增量编译
NOTCHANGED: 当前文件不需处理,甚至复制操作都不用;
ADDED、CHANGED: 正常处理,输出给下一个任务;
REMOVED: 移除outputProvider获取路径对应的文件。
5:transform()方法的参数详解
inputs :消费型输入
referencedInputs:引用型输入,无需输出
isIncremental:是否增量更新
outputProvider:管理输出路径
6. transform 的优化:private WaitableExecutor waitableExecutor;
waitableExecutor =WaitableExecutor.useGlobalSharedThreadPool();
//异步并发处理jar/class
waitableExecutor.execute(()->{
bytecodeWeaver.weaveJar(srcJar,destJar);returnnull;
});
//异步并发处理jar/class
waitableExecutor.execute(()->{
bytecodeWeaver.weaveSingleClassToFile(file,outputFile,inputDirPath);returnnull;
});
//等待所有任务结束
waitableExecutor.waitForTasksWithQuickFail(true);
3.ASM使用
简介:
ASM 可以直接产生二进制的class 文件,也可以在增强既有类的功能。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。
ASM框架中的核心类有以下几个:
ClassReader:该类用来解析编译过的class字节码文件。
ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。
ClassVisitor:主要负责 “拜访” 类成员信息。其中包括标记在类上的注解,类的构造方法,类的字段,类的方法,静态代码块。
AdviceAdapter:实现了MethodVisitor接口,主要负责 “拜访” 方法的信息,用来进行具体的方法字节码操作。
ClassVisitor 实现
MethodVisitor 实现
总结:Android Gradle Plugin 大概讲到这,最后一步Asm实现可以使用Android studio 插件asm-bytecode-outline,它会生成asm实现代码。