Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

安卓平台(已支持 AGP 8+)上的基于 ASM 实现的高性能轻量级字节码全量操作平台,且支持 dexBuilder 任务增量编译。你只需要专注于如何修改字节码达成业务需求,不需要再去写 Gradle 插件和进行复杂且冗余的 IO 处理。

License

Notifications You must be signed in to change notification settings

Ysj001/BytecodeUtil

Repository files navigation

BCU

本库是一个安卓平台上的基于 ASM 实现的轻量级高性能字节码全量操作平台。目前已经兼容 AGP 8+

此外,本库从设计之初就考虑了 Transform 过程对构建时间的影响,因此特别优化了整体构建流程,能更好的利用缓存,增量,过滤机制,从而大幅减少构建所需的时间。

关于构建性能可以直接看文章末尾。

功能&特性

  • v2 版已经支持 AGP 7.4 ~ AGP 8+ | (削除) (v1 老版本是基于 AGP4Transform 接口) (削除ここまで)
  • 完全基于原生 AGPGradle 公开接口开发,没有 Hook 因此不会受到未来 AGP 接口变更影响。
  • 支持根据 variant 进行不同的配置和处理。
  • 支持 dexBuilder 任务的增量编译。
  • 支持非全量 Transform ,可通过 filter 控制需要进行处理的 class。
  • 基于 asm-tree 实现的接口更加易于修改字节码。
  • 插件平台内部多线程并行 IO 处理,提升编译速度。
  • 插件平台支持挂载多个自定义的字节码修改器,使用多个修改器也只会有一次 IO 产生。
  • 提供了 plugin-api 供开发者实现自定义的字节码修改器,甚至不需要再去写 Gradle 插件。

基于 BCU 实现的库

了解&编译项目

BCU 架构图

  • BytecodeUtil

    • app 用于演示
    • buildSrc 管理 maven 发布和版本控制项目统一配置
    • repos 的本地 maven 仓库,便于开发时调试
    • lib_bcu_plugin 插件工程编译时修改字节码在这里实现
      • plugin-api 插件对外提供的 API,基于此可实现自定义的字节码修改器
  • 注意:在构建前先在项目根目录下执行该命令来生成插件后重新 sync 项目

    gradlew publishAllPublicationsToLocalRepository

如何使用

Version & AGP & Gradle

AGP 版本和 Gradle 版本关系点这里

  • v2 版本(推荐使用)
    • 基于 AGP 8+variant#artifacts 接口开发
    • min AGP 7.4 ~ max AGP 8+
    • 当使用 AGP 7.4 时最低应使用 Gradle 7.5 不能使用 Gradle 8
    • 当使用 AGP 8+ 时最低应使用 Gradle 8
  • v1 版本(已经不维护)
    • 基于 AGP 4Transform 接口开发现在不推荐使用了
    • min AGP 4.1.3 ~ max AGP 7.3

使用

  1. 项目已经发到 jitpack.io 仓库,在项目根 build.gradle.kts 中配置如下

    // Top-level build file
    buildscript {
     repositories {
     maven { setUrl("https://jitpack.io") }
     }
     
     dependencies {
     // BCU 插件依赖
     classpath("com.github.Ysj001.BytecodeUtil:plugin:<lastest-version>")
     }
    }
    subprojects {
     repositories {
     maven { setUrl("https://jitpack.io") }
     }
    }
  2. app 模块的 build.gradle.kts 中的配置如下

    plugins {
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
     // 添加 bcu 插件
     id("bcu-plugin")
    }
    // 插件扩展
    bcu {
     config { variant ->
     	// 设置插件日志级别 (0 ~ 5)
     loggerLevel = 2
     	// 挂载你所需的修改器,可以挂载多个,插件内部按顺序执行
     modifiers = arrayOf(
     // Your Modifier
     )
     }
     filterNot = { variant, entryName ->
     	// 自定义过滤规则
     // 该配置会极大影响编译性能,具体见后面性能对比
     // 返回 true 表示该 class 不进行 transform 处理
     false
     }
    }
  3. BCU 会将需要保留的目标加上 BCUKeep 注解,因此只需如下配置即可

    -keepclassmembers class * {
     @com.ysj.lib.bytecodeutil.plugin.api.BCUKeep <methods>;
    }

开发自定义 Modifier 并使用

推荐直接查看 app 模块,或者查看基于 BCU 实现的库。

开发并使用一个自定义的 Modifier 非常简单,主要为以下几个步骤:

  1. 创建一个用于开发 Modifierjava 模块,并依赖 plugin-apigradleApi

    plugins {
     id("java-library")
     id("kotlin")
    }
    dependencies {
     implementation(gradleApi())
     implementation("com.github.Ysj001.BytecodeUtil:plugin-api:<lastest-version>")
    }
  2. 继承 IModifier 接口并重写对应方法

    class CustomModifier(
     // 注意:executor , allClassNode 顺序不能变
     override val executor: Executor,
     override val allClassNode: Map<String, ClassNode>,
    ) : IModifier {
     private val logger = YLogger.getLogger(javaClass)
     override fun initialize(project: Project, variant: com.android.build.api.variant.Variant) {
     super.initialize(project)
     // 初始化阶段,可以通过 project 拿到所需的配置参数
     logger.lifecycle("step1:initialize. variant=${variant.name}")
     // 演示获取自定义参数
     logger.lifecycle(project.properties["modifier.custom"].toString())
     }
     override fun scan(classNode: ClassNode) {
     // 扫描阶段,该阶段可以获取到所有过滤后需要处理的 class
     logger.lifecycle("step2:scan -->$classNode")
     // 你可以在这里收集需要处理的 class
     // 注意:该方法非多线程安全,内部处理记得按需加锁
     }
     override fun modify() {
     // 处理阶段,该阶段是最后一个阶段,用于修改 scan 阶段收集的 class
     logger.lifecycle("step3:modify")
     }
    }
  3. app 模块中使用 bcu-plugin 插件并添加这个自定义的 Modifier

    plugins {
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
     id("bcu-plugin")
    }
    bcu {
     config { variant ->
     // 在这里配置你的 Modifier 实现,多个 Modifier 会按顺序依次执行
     modifiers = arrayOf(
     // 将 CustomModifier 添加到 bcu 中
     CustomModifier::class.java,
     )
     }
    }
    // 演示给 CustomModifier 传递自定义参数
    ext["modifier.custom"] = "这是自定义的参数"

日志解析

了解 BCU 的日志系统有助于帮助你开发出更高性能的 Modifier ,它能够直观的告诉你各个不同阶段的耗时,好让你便于进行优化。

如下图所示 BCU 在运行过程中会执行 2 个任务。

分别为 <variant>BCUTransformTask<variant>BCUAppendTask

BCUAppendTask 负责处理增量而 BCUTransformTask 负责处理整个修改字节码的过程,因此下面的参数解析主要针对该任务中的 log。

log

log 对应的具体含义:

log key 对应含义
>>> loggerLevel 当前 BCU 的日志等级(0~5)
>>> variant 当前变体名称
>>> apply modifier 添加进 BCU 的 Modifier
>>> xxxModifier initialize time xxxModifier 在 initialize 阶段的耗时
>>> bcu scan time BCU 在 scan 阶段的总耗时
>>> xxxModifier process time xxxModifier 在 modify 阶段的耗时
>>> bcu modify time BCU 在 modify 阶段的总耗时
>>> bcu transform output time BCU 将 transform 结果输出到 jar file 的耗时
>>> total process time BCUTransformTask 处理过程的总耗时

关于构建性能

在聊性能前,我们需要先了解下新的 ScopedArtifactsOperation#toTransform 接口造成的问题。

在新 transform 接口中你的输出只能是一个 jar 文件,这会导致如下耗时问题:

  1. class 打进 jar 的操作只能是个单线程的 IO 操作,性能利用率极低。
  2. jar 本质是个 zip 包,默认不配置压缩等级时在 class 文件打入过程还会有额外的压缩计算。
  3. dexBuilderXXX 任务无法处理 jar 输入的增量编译,使得任意代码改动都会造成该任务的全量编译。

本库为了解决这些问题做了如下处理:

  1. 设置 jar 的压缩等级为不压缩,缩短 transform 时的压缩时间和 dexBuilderXXX 时的解压时间。
  2. 提供 filter 接口,减少需要进入 transformclass 数量,加快输出 jar 的速度,且减少由于 jar 导致的 dexBuilderXXX 任务缓存大面积失效问题。
  3. 由于 filter 接口缩减了 transform 输出的源码,因此这部分源码需要借由 ScopedArtifactsOperation#toAppend 接口输出,而基于 append 接口的 Task 本身可以支持增量,并且且输出为直接的 class 文件目录,而这部分输出对 dexBuilderXXX 任务来说是也支持增量的,因此最后所有 filter 出去的 class 就都支持增量编译了。

在了解了本库对提升构建性能所做的处理后,相信你也能更加理解合理配置 fliter 的重要性。

此外由于 filter 后存在额外的计算(使用增量后 gradle 本身会对增量做计算),因此首次编译时构建速度会比不使用 filter 还要慢,但在此之后的编译就能体验到增量的超快速度了,相信这能极大提升你的代码调试效率。

下面是在实际项目上的性能的对比测试,供你参考:

  • 硬件:ROG GU603ZW (32G,1T NVMe)
  • gradle jvmargs:org.gradle.jvmargs=-Xmx8g -Dfile.encoding=UTF-8
  • 代码量:performance_base
全量 增量
performance_1不过滤,全量 performance_2不过滤,任意改动代码触发增量
performance_3过滤后,全量 performance_4过滤后,任意改动代码触发增量

其它

About

安卓平台(已支持 AGP 8+)上的基于 ASM 实现的高性能轻量级字节码全量操作平台,且支持 dexBuilder 任务增量编译。你只需要专注于如何修改字节码达成业务需求,不需要再去写 Gradle 插件和进行复杂且冗余的 IO 处理。

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /