AppInit
AppInit
已有启动框架
Google App StartUp
Google 官方出品,StartUp 提供了简便的依赖任务初始化功能,但是对于一个复杂项目来说,StartUp 有以下不足:
- 不支持异步任务 如果通过 ContentProvider 启动,所有任务都在主线程执行,如果通过接口启动,所有任务都在同一个线程执行
- 不支持组件化 通过 Class 指定依赖任务,需要引用依赖的模块,耦合过高
- 不支持多进程 无法单独配置任务需要执行的进程
- **不支持启动优先级 **虽然可以通过指定依赖来设置优先级,但是过于复杂
App StartUp 更多的是收拢 ContentProvider,对启动优化没啥意义
组件初始化框架演变
自定义接口 + 反射
每个 module 都有一个自己的 Application
- 接口 IAppInit
- 在 app module 中的 AppInitConfig 统一配置所有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
object AppInitConfig {
private val appInitList by lazy {
listOf(
"me.hacket.mylibrary1.MyLib1AppInit",
"me.hacket.mylibrary2.MyLib2AppInit",
"me.hacket.appinitdemo.MainAppInit"
)
}
private val appInitMap by lazy { mutableMapOf<String, IAppInit>() }
fun onCreate(application: Application) {
appInitList.forEach {
try {
val clazz = Class.forName(it)
val obj = clazz.newInstance() as? IAppInit
obj?.let { appInitObj ->
appInitMap[it] = appInitObj
obj.onCreate(application)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
- 在 app 的 Application 中调用
基于 SPI
Service Provider Interface,resources 配置,反射创建实例。
- 在 app 中的
resources/META-INF/services
新建文件 me.hacket.core.IAppInit,内容如下:
1
2
3
me.hacket.appinitdemo.MainAppInit
me.hacket.mylibrary1.MyLib1AppInit
me.hacket.mylibrary2.MyLib2AppInit
- SPI 查找代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
object AppInitSPI {
fun find(application: Application) {
val serviceLoader = ServiceLoader.load(
IAppInit::class.java
)
for (item in serviceLoader) {
item.onCreate(application)
}
application.registerComponentCallbacks(object : ComponentCallbacks2 {
override fun onConfigurationChanged(configuration: Configuration) {
for (item in serviceLoader) {
item.onConfigurationChanged(configuration)
}
}
override fun onLowMemory() {
for (item in serviceLoader) {
item.onLowMemory()
}
}
override fun onTrimMemory(level: Int) {
for (item in serviceLoader) {
item.onTrimMemory(level)
}
}
})
}
}
- Application onCreate 调用
AppInit(自研)
背景
组件化时,由于代码是分了多个 module,module_home,module_share, module_utils,各个组件间需要初始化,且各个 module 之间存在着依赖初始化的关系:module_home 的分享共享依赖 module_share,module_share 依赖 module_utils,如果 module_share 先于 module_utils 初始化,可能导致 module_share 用到了 module_utils 的工具类未初始化导致一些不可预测的的线或 crash 问题。
AppInit 支持功能
- 支持 Application 生命周期分发
- 支持任务的异步
- 支持组件化
- 支持任务依赖
- 支持任务的优先级
- 支持指定任意进程初始化
- 支持 App StartUp
- 支持 KAPT/KSP 生成初始化的 Task
kapt 慢的原因:kapt 有生成 Stub.java 和调用 java apt 两个步骤,生成 Stub 比 apt 速度还要慢;推荐使用 ksp
AppInit 原理
- kapt 收集任务
- ASM 字节码插桩收集到的任务到初始化
- 有向无环图任务依赖
本文由作者按照 CC BY 4.0 进行授权