Gradle Transform和ASM
本文链接:https://blog.youkuaiyun.com/feather_wch/article/details/131753544
关键词:
transform
asm
org.ow2.asm:asm
项目路径:https://github.com/FeatherHunter/StudyNotes/blob/master/assets/DemoProjects/ASMInject.zip
Trasnform
1、Gradle Transform是什么。😊
- Gradle Transform是Android官方提供给开发者在项目构建阶段(.class ->.dex转换期间)用来修改.class文件的一套标准API
- 可以在不修改源代码的情况下,对.class文件进行自定义的操作,例如插入字节码,修改注解,替换资源等。
- Gradle Transform可以用于实现一些高级的功能,例如热修复,代码注入,AOP等。
2、Gradle构建的阶段
- 初始化阶段:Gradle会加载项目的配置信息,检查依赖关系,创建任务图等。
- 编译阶段:Gradle会调用Java编译器将源代码转换成.class文件,并调用AAPT工具将资源文件转换成二进制格式。
- Transform阶段:Gradle会调用Transform API对输入的.class文件进行自定义的操作,例如插入字节码,修改注解,替换资源等。Transform可以用于实现一些高级的功能,例如热修复,代码注入,AOP等。
- DEX阶段:radle会调用DX工具将.class文件转换成DEX文件,以适应Android平台的运行环境。
- 打包阶段:Gradle会调用APK打包器将DEX文件和已编译资源合并成单个APK文件,并对其进行签名和对齐等操作。
- 安装和运行阶段:Gradle会将APK文件安装到目标设备或模拟器上,并启动应用程序。
3、Transform阶段的资源来自于
- class
- jar
- resources:特指和java平级的resources文件夹中资源。不是Android的res文件。
4、> Task :app:preBuild UP-TO-DATE => 代表最新的没有变化
Transform实战
ASMPlugin.groovy:Plugin插件,apply中注册Transform
class ASMPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// 注册Transform ASMTransform
project.extensions.getByType(AppExtension.class).registerTransform(new ASMTransform())
}
}
ASMTransform.groovy:
class ASMTransform extends Transform {
// 1、任务的名称
@Override
String getName() {
return "asm";
}
// 2、可以是Class可以是resources这里只需要Class
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
return TransformManager.CONTENT_CLASS
}
// 3、只对引入插件的模块生效
@Override
Set<? super QualifiedContent.Scope> getScopes() {
return TransformManager.PROJECT_ONLY
}
@Override
boolean isIncremental() {
return false
}
@Override
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
TransformOutputProvider outputProvider = transformInvocation.outputProvider
//清理文件
outputProvider.deleteAll()
def inputs = transformInvocation.inputs
inputs.each {
// 4、因为是transformManager.PROJECT_ONLY 只管当前的src目录,不管jar目录,不需要 it.jarInputs
def directoryInputs = it.directoryInputs
directoryInputs.each {
String dirName = it.name
File src = it.getFile();
println("源目录:" + src)
// 5、transform是一个一个执行的,上一个transoform的输出是下一个的输入。
// 源目录:F:\5.Android\Projects\ASMInject\ASMInject\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes
// 目标目录:F:\5.Android\Projects\ASMInject\ASMInject\app\build\intermediates\transforms\asm\debug\0
String md5Name = DigestUtils.md5Hex(src.absolutePath)
File dest = outputProvider.getContentLocation(dirName + md5Name, // 必须保证是唯一的
it.contentTypes, // 类型
it.scopes, // 作用域
Format.DIRECTORY);
//根据这些参数,outputProvider.getContentLocation 会返回一个 File 对象,表示这个内容的位置。如果格式是 Format.DIRECTORY ,那么结果是一个目录的文件位置;如果格式是 Format.JAR ,那么结果是一个表示要创建的 jar 包的文件。
println("目标目录:" + dest)
println("dirName:" + dirName) // dirName:e47eef71c08b0d42c571e631eb55645c006d92a2
println("md5Name:" + md5Name) // md5Name:5f3e38731d3c990d7ad79d6404c3b796
//插桩
processInject(src, dest);
}
}
}
void processInject(File src, File dest) {
String dir = src.absolutePath
FluentIterable<File> allFiles = FileUtils.getAllFiles(src)
for (File file : allFiles) {
println("需要插桩的文件:" + file.name)