前言
上篇文章 AGP 8.0 路由框架新思路。经过一段时间的使用和反馈,有一个问题是远程依赖的AAR 内部的类没有处理到。这个问题解决完之后发布了两个版本。这篇文章主要说明下使用什么方法解决的。有些兄弟并不知道这个问题已经处理过了,经常还会提起这个问题。单独写篇文章说明一下。
问题所在
上篇文章说到在编译的时候使用 AsmClassVisitorFactory
把的 KSP 生成目录做为参数传递过去,这样极大的减少了编译时间。由于 KSP 生成目录只能是本地的 Module 工程,如果是远程依赖的AAR,是无法把类信息包含在参数里边传递的。所以无法支持远程依赖模块。
聚合生成类
之前版本由于是使用的全量扫描,就生成了不同的模板类,进行插桩的时候去查找不同的类出来,这样太分散了。首先把这个统一成一个Module 类。不管是拦截器添加、页面注册、页面参数都放在这个 Module 类里边,每个模块只有这一个Module 类,这样只用关心这个 Module 类怎么在初始化时候去拿到。
通过 SPI 初始化
KSP 不止可以生成类文件,资源文件也是可以生成的,利用 KSP 生成 SPI 的资源文件,如下图
文件内容只有一行,就是对应 Module 类的全包名:
arduino
com.module.router.app__ModuleRouter__Registered
最终打包的时候资源文件会自动合并在一个文件 :
arduino
com.module.router.app__ModuleRouter__Registered
com.module.router.module_main__ModuleRouter__Registered
com.module.router.module_first__ModuleRouter__Registered
com.module.router.module_two__ModuleRouter__Registered
com.module.router.lib_base__ModuleRouter__Registered
到此在初始化的时候通过 ServiceLoader
就可以取到所有 生成的Modeule 类,包括远程依赖的 ARR。然后遍历并注册 Module。
kotlin
val serviceLoader = ServiceLoader.load(IRouterModule::class.java)
val iterator = serviceLoader.iterator()// 这里返回所有实现 IRouterModule 接口的类集合
try {
while (iterator.hasNext()) {
val module = iterator.next()
registerModule(module)
}
} catch (e: Throwable) {
e.printStackTrace()
}
这样以来,完全避免了字节码插桩,不会对我们编译速度有任何影响。
ServiceLoader
底层是通过反射初始化 Module,有些兄弟可能担心影响应用启动速度。其实这点影响可以忽略的。因为只是通过反射创建了类。并不是 ARouter那样扫描 Dex 文件。扫描Dex 确实会对启动速度有不小的影响,包越大越明显。
ASM 动态开关
如果真是对启动速度要求比较高,不想通过 SPI 初始化。依然可以通过 ASM 初始化。由于ASM 会对影响我们的编译速度。所以增加了一个动态配置的开关。
声明一个 Boolean 类型的开关
kotlin
interface BuildTypeDslExtension {
var openASM: Boolean
}
创建 VariantRouterDslExtension
继承 VariantExtension
编译时来获取配置的开关并赋值。
kotlin
abstract class VariantRouterDslExtension @Inject constructor(extensionConfig: VariantExtensionConfig<*>) :
VariantExtension, java.io.Serializable {
abstract val variantOpenASM: Property<Boolean>
init {
variantOpenASM.set(extensionConfig.buildTypeExtension(BuildTypeDslExtension::class.java).openASM)
}
}
注册 routerConfig
扩展
kotlin
androidComponents.registerExtension(
DslExtension.Builder("routerConfig")
.extendBuildTypeWith(BuildTypeDslExtension::class.java)
.build()
) { config: VariantExtensionConfig<*> ->
project.objects.newInstance(
VariantRouterDslExtension::class.java,
config
)
}
现在我们在 app 模块的 build.gradle
文件的 buildTypes
可以动态配置开关了。
groovy
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
routerConfig { openASM = true } // 开启ASM
}
debug {
minifyEnabled false
routerConfig { openASM = false }// 关闭ASM
}
}
默认是关闭 ASM,会使用 SPI。只有 openASM 为 true 时 才会启用
结尾
最后 我们可以在 debug 时候关闭 ASM,开发过程中不会对我们编译速度有影响。
在打release 包的时候开启ASM。release 本身也基本是全量编译的。
这样即不会影响开发时的编译速度,又可以让线上版本包避免使用反射。