文章

AppInit

AppInit

已有启动框架

Google App StartUp

Google 官方出品,StartUp 提供了简便的依赖任务初始化功能,但是对于一个复杂项目来说,StartUp 有以下不足:

  1. 不支持异步任务 如果通过 ContentProvider 启动,所有任务都在主线程执行,如果通过接口启动,所有任务都在同一个线程执行
  2. 不支持组件化 通过 Class 指定依赖任务,需要引用依赖的模块,耦合过高
  3. 不支持多进程 无法单独配置任务需要执行的进程
  4. **不支持启动优先级 **虽然可以通过指定依赖来设置优先级,但是过于复杂

App StartUp 更多的是收拢 ContentProvider,对启动优化没啥意义

组件初始化框架演变

自定义接口 + 反射

每个 module 都有一个自己的 Application

  1. 接口 IAppInit
  2. 在 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()
            }
        }
    }
}
  1. 在 app 的 Application 中调用

基于 SPI

Service Provider Interface,resources 配置,反射创建实例。

  1. 在 app 中的 resources/META-INF/services 新建文件 me.hacket.core.IAppInit,内容如下:
1
2
3
me.hacket.appinitdemo.MainAppInit
me.hacket.mylibrary1.MyLib1AppInit
me.hacket.mylibrary2.MyLib2AppInit
  1. 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)
                }
            }
        })
    }
}
  1. 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 支持功能

  1. 支持 Application 生命周期分发
  2. 支持任务的异步
  3. 支持组件化
  4. 支持任务依赖
  5. 支持任务的优先级
  6. 支持指定任意进程初始化
  7. 支持 App StartUp
  8. 支持 KAPT/KSP 生成初始化的 Task

kapt 慢的原因:kapt 有生成 Stub.java 和调用 java apt 两个步骤,生成 Stub 比 apt 速度还要慢;推荐使用 ksp

AppInit 原理

  • kapt 收集任务
  • ASM 字节码插桩收集到的任务到初始化
  • 有向无环图任务依赖
本文由作者按照 CC BY 4.0 进行授权