Gradle Transform和ASM

GradleTransform是Android构建过程中用于修改.class文件的API,允许在不修改源码的情况下进行自定义操作,如代码注入和热修复。文章通过ASM库展示了如何在Transform阶段插入字节码,实现方法执行时间的日志打印。ASM是一个强大的字节码操控和分析框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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是什么。😊

  1. Gradle Transform是Android官方提供给开发者在项目构建阶段(.class ->.dex转换期间)用来修改.class文件的一套标准API
  2. 可以在不修改源代码的情况下,对.class文件进行自定义的操作,例如插入字节码,修改注解,替换资源等。
  3. 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阶段的资源来自于

  1. class
  2. jar
  3. 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)
          
<think>嗯,用户问的是如何使用ASMGradle Transform来监控App启动并进行优化。首先,我得回忆一下ASM是什么。ASM是一个Java字节码操作框架,可以用来修改已有的类或者生成新的类。而Gradle Transform是Android Gradle插件中的一个API,允许在构建过程中对类文件进行处理。结合起来,它们可以在编译阶段修改字节码,插入监控代码,从而监控应用的启动过程。 接下来,我需要考虑用户可能的场景。可能是一个Android开发者,想要优化应用的启动时间,尤其是冷启动。他们可能已经知道一些基本的方法,比如减少主线程的工作,懒加载等,但想要更深入的、自动化的监控方案。用户可能希望了解如何实现这种监控,以及具体的步骤工具。 然后,我需要分解问题。首先介绍ASMGradle Transform的基本概念,然后说明如何结合使用它们来监控启动。可能需要解释Transform的工作流程,如何注册Transform,如何用ASM修改字节码,比如在Application的onCreate方法或Activity的onCreate中插入统计代码。同时,需要提到监控启动时间的指标,比如Application初始化时间、首屏渲染时间等。 还要考虑到可能的难点,比如如何定位需要插桩的类方法,如何处理多模块的情况,以及如何避免性能影响。此外,可能还需要提到其他工具,如Android Profiler或Firebase的Startup Tracing,但用户特别问的是ASMGradle Transform,所以重点应放在字节码插桩上。 可能用户还关心实际的效果,比如如何收集数据,是否需要后端服务,或者只是本地日志。此外,启动优化的一些常见策略也可以简要提及,比如异步初始化、延迟初始化等,结合监控数据来指导优化方向。 最后,需要确保回答结构清晰,步骤明确,让开发者能够按照指导去实现。同时,提醒可能遇到的坑,比如ProGuard混淆导致的问题,或Transform的执行顺序,以及兼容性问题。可能还需要提到社区中的一些现有库,比如Hugo或Bytex,但用户可能希望自己实现,所以主要讲解原理步骤。</think>在 Android 开发中,**ASM + Gradle Transform** 的组合常用于实现字节码插桩技术,通过监控应用启动过程中的关键节点来进行性能优化。以下是具体实现原理应用场景的说明: --- ### 一、核心组件解析 1. **ASM** Java 字节码操作框架,可直接修改 `.class` 文件的字节码,用于插入监控代码(如方法耗时统计)。 2. **Gradle Transform** Android 构建流程中的扩展点,允许在编译阶段拦截所有 `.class` 文件(包括第三方库),对字节码进行统一处理。 --- ### 二、启动监控实现步骤 #### 1. **注册自定义 Transform** ```groovy // build.gradle class MyTransform extends Transform { void transform(...) { // 遍历所有输入的 .class 文件 inputs.each { transformInput -> transformInput.jarInputs.each { jarInput -> // 处理 JAR 中的类 } transformInput.directoryInputs.each { dirInput -> // 处理目录中的类 } } } } ``` #### 2. **使用 ASM 插桩关键节点 **目标方法**: - `Application.onCreate()` - `Activity.onCreate()` - `SplashActivity` 生命周期 - 首页 `View` 的绘制完成 **插桩示例**: ```java // ASM 代码:在方法前后插入计时逻辑 public class MethodTimerVisitor extends AdviceAdapter { protected void onMethodEnter() { mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J"); mv.visitVarInsn(LSTORE, startTimeVar); } protected void onMethodExit(int opcode) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J"); mv.visitVarInsn(LLOAD, startTimeVar); mv.visitInsn(LSUB); // 记录耗时到日志或监控系统 } } ``` --- ### 三、监控的关键指标 1. **冷启动时间** - `Application` 初始化耗时 - 首屏 `Activity` 的 `onCreate()` 到 `onWindowFocusChanged()` 2. **热启动时间** - 从后台切换到前台的生命周期耗时 3. **任务调度** - 异步初始化任务(如 `OkHttp`、`Retrofit` 初始化)的阻塞情况 --- ### 四、优化策略 1. **异步初始化** 将非主线程必需的初始化任务(如日志库)放到子线程。 2. **延迟加载** 将非立即使用的资源(如某些 SDK)延后到首页显示后加载。 3. **IdleHandler 优化** 利用 `IdleHandler` 在主线程空闲时执行低优先级任务。 4. **MultiDex 预加载优化** 避免在启动阶段触发 MultiDex 加载。 --- ### 五、工具链扩展 - **数据可视化**:将监控数据上传到 Prometheus/Grafana 或 Firebase - **CI 集成**:在 CI 流程中设置启动耗时阈值,超标时触发告警 - **AB 测试**:对比优化前后的启动时间差异 --- ### 六、注意事项 1. **ProGuard 混淆**:需保持关键类方法名称不被混淆。 2. **Transform 顺序**:通过 `setPriority()` 控制 Transform 执行顺序。 3. **性能平衡**:插桩本身会增加编译时间,需控制插桩范围。 通过 ASM + Transform 的字节码插桩技术,开发者可以精准定位启动瓶颈,结合业务场景制定针对性优化方案,通常可降低 20%~50% 的启动耗时。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值