什么是ASM?
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些class文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
简而言之就是解析class(字节码)文件,修改、生成的新的class(字节码).
###class基本知识
类型描述符:可以理解为类在class内部的名称。
基本类型的描述符:Z表示boolean,C表示char,B表示byte,I表示int,F表示float,J表示long,D表示double。一个类的描述符就是这个类的内部名称,在前面加上一个L,在后面加上一个分号即可。例如,String的类型描述符就是Ljava/lang/String.最后,一个数组的类型描述符就是一个中括号[后面跟上数组元素的类型描述符。
方法描述符
一个方法描述符就是一个包含参数类型的描述符,以及方法返回类型描述符的字符串。一个方法描述符以一个左括号开始,然后跟上每个参数的描述符,然后是一个右括号,最后就是返回值的类型描述符,如果一个方法的返回值是void,那么返回值的类型描述符就是V(一个方法描述符不包含这个方法的名称以及参数的名称).
ASM核心类介绍
主要核心类:
* ClassReader 一个字节码读取和分析器,类似SAX事件流的一种读取机制,当发生一些时间时就会调用相关的visitor进行相应的处理
* ClassVisitor
* FieldVisitor
* MethodVisitor
* AnnotationVisitor
* ClassWriter类:它实现了ClassVisitor接口,用于拼接字节码。
来我们来写一个类
ClassWriter cw = new ClassWriter(0); |
接下我们来把方法实现一下
//生成方法 |
解析一个class文件的基本流程:...获取class字节流或字节数组
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new XXXClassVisitor(cw);
cr.accept(cv, EXPAND_FRAMES);
//获取修改后的字节数据
byte[] code = cw.toByteArray();
...保存成的class文件
ClassVisitor cv = new ClassVisitor(Opcodes.ASM4, cw) { |
MethodVisitor mv = new MethodVisitor(api, mv) { |
Android上的如何使用
gradle plugin 的Transform Api
Transform允许第三方插件在class文件转为为dex文件前操作编译好的class文件,拿到正常的class后再经过ASM插入字节码后得到新的class,再被dx转成dex。
public class TestAsmPlugin extends Transform implements Plugin { |
- dex相关Task前插入操作字节码的任务
transformClassesWithDexForXXX.doFirst { |
###应用场景
无埋点统计、APM、插桩
其实就是在上面的基础进行各种位置的插桩,具体例子Android无埋点数据收集SDK关键技术
瘦包
蘑菇街的ThinRPlugin插件
相关原理:android中的R文件,除了styleable类型外,所有字段都是int型变量/常量,且在运行期间都不会改变。所以可以在编译时,记录R中所有字段名称及对应值,然后利用asm工具遍历所有class,将引用R字段的地方替换成对应常量,然后将R$styleable.class以外的所有R.class删除掉
BTW:类似瘦包的思路:Facebook redex(不是使用asm)