写这篇文章的目的有三个原因,首先在 ByteX
的交流群里有同学反馈源码不易阅读,看起来比较费力,所以希望通过自己的理解和梳理能够帮助大家学习 ByteX
的源码。其次,有同学在阅读我的博客 AOP 利器 ASM 基础入门 之后反馈,不知道如何在工作中发现 ASM
的使用场景,所以希望借助 ByteX
的插件能给大家一些应用场景的启发。最后是作为个人学习 ASM
的一个总结。
1. ByteX 介绍
ByteX
是一个基于 Gradle Transform API
和 ASM
的字节码插件平台。通过平台化的设计可以避免针对每个 Feature
业务单独开发一个插件造成的编译构建耗时线性增加和代码耦合臃肿。目前 ByteX
集成了若干个字节码插件,每个插件完全独立,既可以脱离 ByteX
这个宿主而独立存在,又可以自动集成到宿主和其它插件一起整合为一个单独的 Transform
。插件和插件之间,宿主和插件之间的代码是完全解耦的,这使得 ByteX
在代码上拥有很好的可拓展性,新插件的开发将会变得更加简单高效。
ByteX 仓库地址:https://github.com/bytedance/ByteX
2. 项目结构
通过对 Plugin
和 Transform
的抽象,将公共的代码剥离到 common
库中,提供给所有的插件复用,这样每个插件就只需要关注字节码插桩的业务逻辑处理即可。同时插件间自动无缝地整合成一个 Transform
,提高构建的效率。Transform
过程中,对 class
文件的 IO
是比较耗时的,把所有的 plugin
整合成一个单独 Transform
可以避免打包的额外时间开销呈线性增长。让耗时从 1+1=2
,变成1+1<2
或者约等于 1
。平台化每个插件之间的代码相互隔离,避免代码的耦合,方便扩展。
2.1 Plugins 应用层
在 ByteX
项目中,提供了诸多插件。
- access-inline-plugin:
access
方法内联优化,减少apk
方法数和包大小 - shrink-r-plugin:
R
文件瘦身和无用资源检查 - closeable-check-plugin:文件流的
close
检查 - const-inline-plugin:常量内联
- field-assign-opt-plugin:优化多余赋值指令
- getter-setter-inline-plugin :
getter
和setter
方法内联 - method-call-opt-plugin:干净地删除某些方法调用,如
Log.d
- coverage-plugin:线上代码覆盖率
- refer-check-plugin:检查是否有调用不存在的方法和引用不存在的字段
- serialization-check-plugin:序列化检查
- SourceFileKiller:删除
SourceFile
和行号属性 - ButterKnifeChecker:检测跨
module
使用ButterKnife
可能导致的问题 - RFileKnife:修复
R.java
太大编译报code too large
的问题
当然我们也可以基于 common
库开发自定义插件。这里可以参照研发 WIKI
:Developer API。
2.2 Common 层
common
模块是整个框架的核心模块,它封装了公共代码部分,供所有插件使用。结构大体可分为如下:
- 通用插件
BasePlugin
的抽象,涉及到的类有:AbsMainProcessPlugin
、AbsPlugin
、MainProcessHandler
和IPlugin
- 插件配置项的抽象,涉及到的类有:
BaseExtension
- 通用
Transform
的抽象,涉及到的类有:CommonTransform
、SimpleTransform
、ProxyTransform
- 通用的
ClassVisitor
,涉及到的类有:BaseClassVisitor
、ClassVisitorChain
- 编译生命周期监听:
ByteXBuilderListener
、TransformFlowListener
、PluginStatus
- 类图的生成:涉及到
flow
和graph
两个包 - 类分析器和校验:
ClassFileAnalyzer
和ClassFileTransformer
、AsmVerifier
、AsmVerifyClassVisitor
很多同学反馈 common
库比较复杂,通过上面的模块拆分,能更好辅助阅读 common
层的源码。
2.3 底层依赖
在 common
层中,依赖 ASM
仓库、TransformEngine
包和 GradleToolKit
来实现。
3. Common 模块核心逻辑
common
模块是整个框架的核心,包含了公共代码,是我们重点研读的对象。
3.1 核心流程
无论如何变化,基于 Transform
机制开发的插件,大体核心的流程都是从 Plugin.apply()
方法开始。 ByteX
核心流程参见下图。
3.2 AbsPlugin.apply 方法
AbsPlugin.apply()
是自定义插件的关键步骤,通常包含插件配置项的定义、回调监听以及注册 Transform
处理。
public final void apply(@NotNull Project project) {
// 回调 onByteXPluginApply 监听
GlobalByteXBuildListener.INSTANCE.onByteXPluginApply(project, this);
this.project = project;
this.andro