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

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

相关推荐
androidwork8 小时前
Android LinearLayout、FrameLayout、RelativeLayout、ConstraintLayout大混战
android·java·kotlin·androidx
每次的天空8 小时前
Android第十三次面试总结基础
android·面试·职场和发展
wu_android8 小时前
Android 相对布局管理器(RelativeLayout)
android
李斯维10 小时前
循序渐进 Android Binder(二):传递自定义对象和 AIDL 回调
android·java·android studio
androidwork10 小时前
OkHttp 3.0源码解析:从设计理念到核心实现
android·java·okhttp·kotlin
像风一样自由10 小时前
【001】frida API分类 总览
android·frida
casual_clover10 小时前
Android 之 kotlin 语言学习笔记四(Android KTX)
android·学习·kotlin
移动开发者1号12 小时前
Android 大文件分块上传实战:突破表单数据限制的完整方案
android·java·kotlin
移动开发者1号12 小时前
单线程模型中消息机制解析
android·kotlin
每次的天空15 小时前
Android第十五次面试总结(第三方组件和adb命令)
android