AGP 8 路由最终解决方案

前言

上篇文章 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 本身也基本是全量编译的。

这样即不会影响开发时的编译速度,又可以让线上版本包避免使用反射。

相关推荐
JhonKI7 小时前
【MySQL】存储引擎 - CSV详解
android·数据库·mysql
开开心心_Every7 小时前
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
android·windows·python·搜索引擎·智能手机·pdf·音视频
大G哥8 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
鸿蒙布道师11 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork12 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
xiangxiongfly91514 小时前
Android setContentView()源码分析
android·setcontentview
人间有清欢15 小时前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
人间有清欢16 小时前
Android开发报错解决
android
每次的天空17 小时前
Android学习总结之kotlin协程面试篇
android·学习·kotlin
每次的天空19 小时前
Android学习总结之Binder篇
android·学习·binder