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

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

相关推荐
MiyamuraMiyako18 分钟前
从 0 到发布:Gradle 插件双平台(MavenCentral + Plugin Portal)发布记录与避坑
android
NRatel1 小时前
Unity 游戏提升 Android TargetVersion 相关记录
android·游戏·unity·提升版本
叽哥3 小时前
Kotlin学习第 1 课:Kotlin 入门准备:搭建学习环境与认知基础
android·java·kotlin
风往哪边走4 小时前
创建自定义语音录制View
android·前端
用户2018792831674 小时前
事件分发之“官僚主义”?或“绕圈”的艺术
android
用户2018792831674 小时前
Android事件分发为何喜欢“兜圈子”?不做个“敞亮人”!
android
Kapaseker5 小时前
你一定会喜欢的 Compose 形变动画
android
QuZhengRong6 小时前
【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
android·数据库·excel
zhangphil7 小时前
Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin(2)
android·kotlin
程序员码歌13 小时前
【零代码AI编程实战】AI灯塔导航-总结篇
android·前端·后端