思路:在点击事件onclick的时候,将view的onclick在给定的时间给拦截掉。以前我们可能都是用一个util来拦截,这样在每个点击事件都得去判断,那么这里就用字节码插桩的形式来实现一下。
ASM的引入
dependencies { implementation gradleApi() implementation localGroovy() //常用io操作 implementation "commons-io:commons-io:2.6" // Android DSL Android编译的大部分gradle源码 implementation 'com.android.tools.build:gradle:3.6.2' implementation 'com.android.tools.build:gradle-api:3.6.2' //ASM implementation 'org.ow2.asm:asm:7.1' implementation 'org.ow2.asm:asm-util:7.1' implementation 'org.ow2.asm:asm-commons:7.1' }
常用的对象
- ClassReader : 按照Java虚拟机规范中定义的方式来解析class文件中的内容,在遇到合适的字段时调用ClassVisitor中相应的方法
- ClassVisitor : Java中类的访问者,提供一系列方法由ClassReader调用.它是一个抽象类,在使用时需要继承此类.
- ClassWriter : 它是一个继承了ClassVisitor的类,主要负责将ClassReader传递过来的数据写到一个字节流中.在传递数据完成之后,可以通过它的toByteArray方法获得完整的字节流.
- ModuleVisitor : Java中模块的访问者,作为ClassVisitor.visitModule方法的返回值,要是不关心模块的使用情况,可以返回一个null.
- AnnotationVisitor : Java中注解的访问者,作为ClassVisitor.visitTypeAnnotation的返回值,不关心注解使用情况也是可以返回null.
- FieldVisitor : Java中字段的访问者,作为ClassVisitor.visitField的返回值,不关心字段使用情况也是可以返回null.
- MethodVisitor:Java中方法的访问者,作为ClassVisitor.visitMethod的返回值,不关心方法使用情况也是可以返回null
ASM工作流程
通过ClassReader读取class字节码文件,然后ClassReader将读取到的数据通过一个ClassVisitor(上面的ClassWriter其实就是一个ClassVisitor)将数据表现出来.表现形式: 将字节码的每个细节按顺序通过接口的方式传递给ClassVisitor.就比如说,访问到了class文件的xx方法,就会回调ClassVisitor的visitMethod方法;访问到了class文件的属性,就会回调ClassVisitor的visitField方法.
ClassWriter是一个继承了ClassVisitor的类,它保存了这些由ClassReader读取出来的字节流数据,最后通过它的toByteArray方法获得完整的字节流
具体代码
主要技术点: Gradle plugin + transform + annotation + ASM
Gradle plugin
Gradle transform
annotation
ASM
代码测试
反编译查看
可以看到,正常的onclick写法是可以插桩成功
,添加注解时可以成功跳过插桩
的,lamda表达式这种情况下是无法插桩成功
的,生成的class方法名上没有onclick标志,找不到什么共同点,这个暂时没有想到什么好的办法,但是现在基本都是kotlin代码了,所以这种形式感觉还是有些鸡肋了。