一、什么是插桩
插桩:目标程序代码中某些位置插入或修改一些代码,从而在目标程序运行过程中获取某些程序状态并加以分析。简单来说就是在代码中插入代码。 函数插桩:便是在函数中插入或修改代码,在Android编译过程中,往字节码里插入自定义的字节码,所以也可以称为字节码插桩。
二、如何进行操作
完成插桩需要考虑以下两步:
1、寻找插入时机
了解Android打包流程
2、插入字节码
2.1、插桩入口 Transform API:Android Gradle提供,可操作获取字节码
2.1.1、自定义Transform、插件编写
2.2、字节码操作 Javassist :一个开源的分析、编辑和创建 Java 字节码的类库 ASM:Java 字节码操控框架 ASM 的优点就在于性能上的优势,且更加灵活;Javassist 的实现 中大量使用的反射,所以性能偏低。这里使用ASM来处理字节码。
2.1、Transform 在class编译成dex的过程中,会经过一系列Transform处理。Transform之间采用流式处理方式。每个Transform需要一个输入,处理完成后产生一个输出,而这个输出又会作为下一个Transform的输入。就这样,所有的Transform依次完成自己的使命。Proguard、Muti-Dex等功能都是通过继承Transform实现的,而我们自定义的Transform,会插入到这个Transform链条的最前面。
2.1.1、自定义Transform:
1 、继承Transform(看下实际项目讲解几个重要方法) 2、自定义gradle插件,集成Transform
Gradle自定义插件 插件的源码可以使用Scale、groovy、java、kotlin编写,有三种地方(方式)可以自定义:
1、直接在build.gradle中编写
2、在项目的 buildSrc 目录下编写
3、创建一个Android Library Module,可以打包发布 JAR。
这里介绍第三种做法:
1、创建一个Library Module
2、修改build.gradle文件
3、修改src/main/java的目录名改成src/main/groovy
4、在groovy新建自己的包目录,并新建一个groovy 类,这个类就是插件的入口类
5、新建插件配置文件 /src/main/resources/META-INF/gradle-plugins/xx.properties,文件名xx就是插件名,引用插件时使用apply plugin: "xx"
6、执行uploadArchives这个gradle任务会在 项目根目录的repo文件夹生成一个本地maven仓库
7、在项目的gradle文件添加仓库引用
8、在 app目录下的gradle文件添加apply plugin: 引用插件
2.2、ASM
ASM不是直接对数字字节码进行操作,而是对类似于”com/rhythm7/Main.m:I“这种字节码反编译后的格式进行操作,这样的格式,就友好的多,我们无需关注class文件冗长的数字中方法的偏移量、编码方式、指代含义等,只需要关注字节码指令即可。
ASM提供很多vistor接口供我们使用,在 ASM 中,提供了一个 ClassReader类,这个类可以直接由字节数组或由 class 文件间接的获得字节码数据,它能正确的分析字节码,构建出抽象的树在内存中表示字节码。它会调用 accept方法,这个方法接受一个继承了 ClassVisitor抽象类的对象实例作为参数,然后依次调用 ClassVisitor接口的各个方法。
使用ASM Bytecode Outline插件生成ASM代码 1、在Android Studio中安装ASM Bytecode Outline插件 2、安装后,在编译器中,点击右键,选择Show Bytecode outLine 3、在ASM标签中选择ASMified,即可在右侧看到当前类对应的ASM代码。
三、在Android中的应用
目前想到的是编译时AOP方向,做一些统一的管理控制。
1、行为统计 页面的打开关闭、点击事件
2、方法耗时统计
3、方法替换(热修复等)
4、多模块解耦
...... 其他方向:
1、对第三方jar包、依赖做修改(修复bug等)
2、对系统方法修改(自定义全局Log输出、方法适配等) ......