前言
Transform API
是 AGP1.5
就引入的特性,主要用于在 Android
构建过程中,在 Class
转Dex
的过程中修改 Class
字节码。利用 Transform API
,我们可以拿到所有参与构建的 Class
文件,然后可以借助ASM
等字节码编辑工具进行修改,插入自定义逻辑。
国内很多团队都或多或少的用 AGP
的 Transform API
来搞点儿黑科技,比如无痕埋点,耗时统计,方法替换等。但是在AGP7.0
中Transform
已经被标记为废弃了,并且将在AGP8.0
中移除。而AGP8.0
应该会在今年内发布,可以说是已经近在眼前了。所以现在应该是时候了解一下,在Transform
被废弃之后,该怎么适配了。
Transform Action
介绍
Transform API
是由AGP
提供的,而Transform Action
则是由Gradle
提供。不光是 AGP
需要 Transform
,Java
也需要,所以由 Gradle
来提供统一的 Transform API
也合情合理。
这应该也是Transform API
被废弃的原因,既然Gradle
已经统一提供了API
,AGP
也就没必要自定义一套了。
关于 TransformAction
如何使用,Gradle
官方已经提供了很详细的文档–Transforming dependency artifacts on resolution,具体使用可以直接参考文档
AsmClassVisitorFactory
介绍
直接使用Transform Action
的话还是有些麻烦,跟Transform API
一样,需要手动处理增量编译的逻辑。AGP
很贴心的为我们又做了一层封装,提供了AsmClassVisitorFactory
来方便我们使用Transform Action
进行ASM
操作。 根据官方的说法,AsmClassVisitoFactory
会带来约18%的性能提升,同时可以减少约5倍代码
代码实战
接下来我们利用AGP
的AsmClassVisitorFactory API
,来实现方法执行耗时的插桩。
实现AsmClassVisitorFactory
abstract class TimeCostTransform: AsmClassVisitorFactory<InstrumentationParameters.None> {
override fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor {
return TimeCostClassVisitor(nextClassVisitor)
}
override fun isInstrumentable(classData: ClassData): Boolean {
return true
}
}
-
AsmClassVisitorFactory
即创建ClassVisitor
对象的工厂。此接口的实现必须是一个抽象类, -
createClassVisitor
返回我们自定义的ClassVisitor
,在自定义Visitor
处理完成后,需要传内容传递给下一个Visitor
,因此我们将其放在构造函数中传入 -
isInstrumentable
用于控制我们的自定义Visitor
是否需要处理这个类,通过这个方法可以过滤我们不需要的类,加快编译速度
自定义ClassVisitor
class TimeCostClassVisitor(nextVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM5, nextVisitor) {
override fun visitMethod(
access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>?
): MethodVisitor {
val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)
val newMethodVisitor =
object : AdviceAdapter(Opcodes.ASM5, methodVisitor, access, name, descriptor) {
@Override
override fun onMethodEnter() {
// 方法开始
if (isNeedVisiMethod(name)) {
mv.visitLdcInsn(name);
mv.visitMethodInsn(
INVOKESTATIC, "com/zj/android_asm/TimeCache", "putStartTime","(Ljava/lang/String;)V", false
);
}
super.onMethodEnter();
}
@Override
override fun onMethodExit(opcode: Int) {
// 方法结束
if (isNeedVisiMethod(name)) {
mv.visitLdcInsn(name);
mv.visitMethodInsn(
INVOKESTATIC, "com/zj/android_asm/TimeCache", "putEndTime","(Ljava/lang/String;)V", false
);
}
super.onMethodExit(opcode);
}
}
return newMethodVisitor
}
private fun isNeedVisiMethod(name: String?):Boolean {
return name != "putStartTime" && name != "putEndTime" && name != "<clinit>" && name != "printlnTime" && name != "<init>"
}
}
这里就跟普通的ASM
操作没什么不同了,主要是通过ASM
字节码插桩,在方法的前后插入如下代码,通过计算两者的时间差来得出方法的耗时
fun timeMethod(){
TimeCache.putStartTime("timeMethod") //方法开始插入的代码
Thread.sleep(1000)
TimeCache.putEndTime("timeMethod") //方法结束插入的代码
}
注册Transform
老版本的Transform
是注册在AppExtension
中的,新版本则是注册在AndroidComponentsExtension
中
class TimeCostPlugin : Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.onVariants { variant ->
variant.instrumentation.transformClassesWith(TimeCostTransform::class.java,
InstrumentationScope.PROJECT) {}
variant.instrumentation.setAsmFramesComputationMode(
FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
)
}
}
}
- 基于
variant
可实现不同的变种不同的处理逻辑 -
transformClassesWith
通过InstrumentationScope
控制是否需要扫描依赖库代码 -
setAsmFramesComputationMode
可设置不同的栈帧计算模式,具体可查看源码
AsmClassVisitorFactory
的优势
通过以上步骤,一个简单的通过插桩计算方法执行耗时的功能就完成了,这比起老版的Transform API
其实简化了不少,老版本处理增量更新就需要处理一大堆的逻辑。
可以看到我们这里并没有手动处理增量逻辑,这是因为调用AsmClassVisitorFactory
的TransformClassesWithAsmTask
继承自NewIncrementalTask
,已经处理了增量逻辑,不需要我们再手动处理了
同时老版本的Transform
每个Transfrom
各自独立,如果每个Transform
编译构建耗时+10s
,各个Transform
叠在一起,编译耗时就会呈线性增长
而新版本可以看出我们也没有手动进行IO
操作,这是因为AsmInstrumentationManager
中已经做了统一处理,只需要进行一次IO
操作,然后交给ClassVisitor
链表处理,完成后统一交给ClassWriter
写入
通过这种方式,可以有效地减少IO
操作,这也是新版本API
性能提升的原因
总结
总得来说,由于Transform API
在AGP7.0
已标记为废弃,并且将在AGP8.0
中移除,是时候了解一下如何迁移Transform API
了
而AsmClassVisitorFactory
相比Transform API
,使用起来更加简单,不需要手动处理增量逻辑,可以专注于字节码插桩操作。同时AsmClassVisitorFactory
通过减少IO
的方式,可以得到约20%的性能提升,加快编译速度。
最后,保持好的开发习惯,砍掉不必要的功能才是保证包体积持续优化的超级大招,这里再给大家分享一份一线大厂开发都有的 《Android 核心笔记汇总》,如有需要参考的可以点击这里查看更多 传送门直达!!!
网友评论