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

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

相关推荐
冬奇Lab2 小时前
Android 15音频子系统(七):音量控制系统深度解析
android·音视频开发
方白羽6 小时前
Android NFC 功能集成-读卡器模式
android·app·客户端
进击的cc6 小时前
Android Kotlin:委托属性深度解析
android·kotlin
进击的cc6 小时前
Android Kotlin:Kotlin数据类与密封类
android·kotlin
恋猫de小郭7 小时前
你的蓝牙设备可能正在泄漏你的隐私? Bluehood 如何追踪附近设备并做隐私分析
android·前端·ios
私人珍藏库7 小时前
[Android] 卫星地图 共生地球 v1.1.22
android·app·工具·软件·多功能
冰珊孤雪8 小时前
Android Studio Panda革命性升级:内存诊断、构建标准化与AI调试全解析
android·前端
_李小白9 小时前
【OSG学习笔记】Day 23: ClipNode(动态裁剪)
android·笔记·学习
Eagsen CEO9 小时前
如何让 Gemini 在 Android Studio 中顺利工作
android·ide·android studio
ywf121510 小时前
FlinkCDC实战:将 MySQL 数据同步至 ES
android·mysql·elasticsearch