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

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

相关推荐
流星白龙1 小时前
【MySQL高阶】21.撤销表空间,撤销日志
android·mysql·adb
我命由我123452 小时前
Android 开发,FragmentPagerAdapter 的 isViewFromObject 方法问题
android·java-ee·kotlin·android studio·android jetpack·android-studio·android runtime
weiggle3 小时前
第五篇:Modifier 解析——链式调用的艺术
android
awu的Android笔记3 小时前
Android 弱网模拟:别只会用均匀分布——三种延迟模型和两种丢包模型的原理与实现
android·tcp/ip
sensor_WU3 小时前
【Delphi】 开发 android 升级模块硬核实现
android·delphi android·android 升级·apk升级 delphi
帅次4 小时前
Kotlin MVVM 实战入门:从分层到状态闭环
android·kotlin·android studio·android jetpack
YF02114 小时前
Android BLE 信号强度获取与 底层原理深度解析
android·蓝牙
随遇丿而安4 小时前
第7周:RecyclerView 高级功能与列表硬核优化
android
qq3621967054 小时前
手机App下载安装完全指南:2026最新教程(Android & iOS)
android·ios·智能手机
想取一个与众不同的名字好难4 小时前
安卓设置亮度的时候,系统会在100%与0%反复横跳
android·java·开发语言