前言

Transform APIAGP1.5 就引入的特性,主要用于在 Android 构建过程中,在 ClassDex的过程中修改 Class 字节码。利用 Transform API,我们可以拿到所有参与构建的 Class 文件,然后可以借助ASM 等字节码编辑工具进行修改,插入自定义逻辑。

国内很多团队都或多或少的用 AGPTransform API 来搞点儿黑科技,比如无痕埋点,耗时统计,方法替换等。但是在AGP7.0Transform已经被标记为废弃了,并且将在AGP8.0中移除。而AGP8.0应该会在今年内发布,可以说是已经近在眼前了。所以现在应该是时候了解一下,在Transform被废弃之后,该怎么适配了。

Transform Action介绍

Transform API是由AGP提供的,而Transform Action则是由Gradle提供。不光是 AGP 需要 TransformJava 也需要,所以由 Gradle 来提供统一的 Transform API 也合情合理。
这应该也是Transform API被废弃的原因,既然Gradle已经统一提供了APIAGP也就没必要自定义一套了。

关于 TransformAction 如何使用,Gradle 官方已经提供了很详细的文档–Transforming dependency artifacts on resolution,具体使用可以直接参考文档

AsmClassVisitorFactory介绍

直接使用Transform Action的话还是有些麻烦,跟Transform API一样,需要手动处理增量编译的逻辑。AGP很贴心的为我们又做了一层封装,提供了AsmClassVisitorFactory来方便我们使用Transform Action进行ASM操作。 根据官方的说法,AsmClassVisitoFactory会带来约18%的性能提升,同时可以减少约5倍代码

代码实战

接下来我们利用AGPAsmClassVisitorFactory 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}
}
  1. AsmClassVisitorFactory即创建ClassVisitor对象的工厂。此接口的实现必须是一个抽象类,
  2. createClassVisitor返回我们自定义的ClassVisitor,在自定义Visitor处理完成后,需要传内容传递给下一个Visitor,因此我们将其放在构造函数中传入
  3. 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) {@Overrideoverride fun onMethodEnter() {// 方法开始if (isNeedVisiMethod(name)) {mv.visitLdcInsn(name);mv.visitMethodInsn(INVOKESTATIC, "com/zj/android_asm/TimeCache", "putStartTime","(Ljava/lang/String;)V", false);}super.onMethodEnter();}@Overrideoverride 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)}}
}
  1. 基于variant可实现不同的变种不同的处理逻辑
  2. transformClassesWith通过InstrumentationScope控制是否需要扫描依赖库代码
  3. setAsmFramesComputationMode可设置不同的栈帧计算模式,具体可查看源码

AsmClassVisitorFactory的优势

通过以上步骤,一个简单的通过插桩计算方法执行耗时的功能就完成了,这比起老版的Transform API其实简化了不少,老版本处理增量更新就需要处理一大堆的逻辑。
可以看到我们这里并没有手动处理增量逻辑,这是因为调用AsmClassVisitorFactoryTransformClassesWithAsmTask继承自NewIncrementalTask,已经处理了增量逻辑,不需要我们再手动处理了

同时老版本的Transform每个Transfrom各自独立,如果每个Transform编译构建耗时+10s,各个Transform叠在一起,编译耗时就会呈线性增长
而新版本可以看出我们也没有手动进行IO操作,这是因为AsmInstrumentationManager中已经做了统一处理,只需要进行一次IO操作,然后交给ClassVisitor链表处理,完成后统一交给ClassWriter写入
通过这种方式,可以有效地减少IO操作,这也是新版本API性能提升的原因

总结

总得来说,由于Transform APIAGP7.0已标记为废弃,并且将在AGP8.0中移除,是时候了解一下如何迁移Transform API

AsmClassVisitorFactory相比Transform API,使用起来更加简单,不需要手动处理增量逻辑,可以专注于字节码插桩操作。同时AsmClassVisitorFactory通过减少IO的方式,可以得到约20%的性能提升,加快编译速度。

最后,保持好的开发习惯,砍掉不必要的功能才是保证包体积持续优化的超级大招,这里再给大家分享一份一线大厂开发都有的 《Android 核心笔记汇总》,如有需要参考的可以私信回复 666 即可取货!!!

Transform 被废弃,ASM 如何适配?相关推荐

  1. 【我的ASM学习进阶之旅】 介绍一个基于gradle transform api和ASM的字节码插件平台ByteX

    原文链接: https://github.com/bytedance/ByteX/blob/master/README_zh.md 文章目录 ByteX(Infinite Possibilities) ...

  2. 解决web前端高分辨率电视(4K)适配问题

    因为自己没适配过电视,所以页面是按照3840*2610开发的,但是用电视打开后发现分辨率变了,变成了960*540.一下给我整不会了. 想了想使用了transform对页面进行了适配 此方法可以用于高 ...

  3. 【金三银四】2022 Android面经实录

    1. 前言 春水初盛,垂钓者络绎不绝,鱼儿按捺不住,拍打着尾鳍纷纷跃出水面,沽个好价. 本篇真实的记录了我从 准备->复习->面试 的全过程,分享一些我的真实经验,希望能帮到大家. 2. ...

  4. 【vue】vue-znly

    老规矩,放下博主的项目地址:https://github.com/wohaiwo/vue-znly 我一直在想给那些开源者取什么名字比较好,怎样才对得起他们开源项目的精神,后来想想,还是叫博主吧.有的 ...

  5. spring boot 2.x静态资源会被HandlerInterceptor拦截的原因和解决方法

    转载:https://my.oschina.net/dengfuwei/blog/1795346 spring boot 2.x静态资源会被HandlerInterceptor拦截的原因和解决方法  ...

  6. 货拉拉 Android 动态资源管理系统原理与实践(下)

    点击上方蓝字关注我,知识会给你力量 so资源动态化方案 so资源打包问题 在打包so资源的过程中,我们遇到了如下问题. 如何移除apk中的so文件,并将他们收集起来? 如何将多个so文件压缩打包,并生 ...

  7. css鼠标划过时的一些小特效

    css部分----------- .div1{width: 100px; height: 100px; border: 1px solid; position: relative; backgroun ...

  8. 技术漫谈之——Jectpack Compose

    最近Jetpack Compose发布了Beta版本,抽时间了解了一下Compose带来的改变和其中的一些原理.本文不会讲解具体API,只是比较随意的分享自己的一些疑问以及在探寻答案过程中的一些收获. ...

  9. 在线调试工具 Arthas

    Arthas(阿尔萨斯) 是 Alibaba开源的一款 Java在线诊断工具,能够分析,诊断,定位Java应用问题,例如:JVM信息,线程信息,搜索类中的方法,跟踪代码执行,观测方法的入参和返回参数等 ...

最新文章

  1. 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?
  2. 中国开封菊花花会照片1
  3. win8: 清除iframe的缓存
  4. 小尼机器人_小尼被机器人嫌弃“唱歌难听,长相一般”?
  5. VC 忽略警告的方法
  6. 8 stark组件 展示数据
  7. 加快android编译速度
  8. 批处理写的关机小程序--bat
  9. UVa1586 - Molar mass
  10. 安卓基础干货(四):安卓网络编程的学习
  11. 2015上海网络赛 HDU 5475 An easy problem 线段树
  12. 渗透场景篇--当XSS遇上CSRF
  13. bootstrap项目中无间距栅格(grid) no-gutter
  14. MyBatis源码阅读(十) --- 一级缓存、二级缓存工作原理
  15. 四年级计算机上册课程标准,新课程标准人教版四年级上册数学全册教案设计
  16. C++ 虚函数实现:虚函数表 虚表指针
  17. JAVA:实现Lucas Series卢卡斯系列算法(附完整源码)
  18. UOJ #34 多项式乘法
  19. 旧电脑装什么系统最快_【老旧电脑装什么系统】老电脑装什么系统流畅_老旧电脑收藏...
  20. mapstruct使用指南(2)

热门文章

  1. 我的前端学习历程,你认同吗
  2. 【图形图像处理】之位图图像和矢量图形有何区别?
  3. Lammps切削过程实现及工件表面识别
  4. 珍惜身边所有,因为来日不方长。
  5. 【2013年度CSDN博客之星】评奖:全文五言句,俺也拉个票
  6. android 伪终端,伪终端(pty)机制祥解
  7. 随便学学Python-day10-函数的进阶
  8. 2020年的创业趋势是什么?未来适合什么行业钱赚的多?[附十六大趋势]
  9. 2013年阿里巴巴实习生招聘笔试题目及解答
  10. css的grid布局