App Startup

App StartupJetpack的新成员,官方声明这是一个在 Android 应用启动时,针对初始化组件进行优化的依赖库,提供了一种在应用程序启动时初始化组件的简单、高效的方法。库开发者和应用开发者都可以使用app Startup来简化启动序列,并显式地设置初始化顺序。

App 启动初始化方式

Application.onCreate()

App 引用的库或SDK或其他组件,很多在使用前都需要进行初始化,这个过程一般会放在 ApplicationonCreate() 中,当需要初始化的组件越来越多时,可能会出现:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        A.init(this)
        B.init(this)
        C.init(this)
        D.init(this)
        E.init(this)
    }
    ...
}

init() 一连出现几十行时,会显得既凌乱又多余。

ContentProvider

ContentProvider 作为四大组件之一,主要作用是跨应用程序共享数据。它的 onCreate 调用在 Application.attachBaseContext()Application.onCreate() 之间。所以库开发者们想到借助 ContentProvider 来进行初始化。

class MyProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        context?.let {
            A.init(it)
        }
        return true
    }
    ...
}

继承 ContentProvider 并在 onCreate() 中完成初始化操作,然后在 AndroidManifest 中注册,那么当应用启动时,会自动调用这个 ContentProvideronCreate() 从而完成初始化过程。这样使用者不再需要在 Application 显式写入初始化逻辑,也就是可对其“无感”。

但是这种方式存在性能问题,ContentProvider 作为四大组件之一,是相对比较重量级的,可能一个本身很轻量级的初始化过程,通过这种方式就会变成重量级操作,而如果有几十个这样的初始化需求,各自创建了一个 ContentProvider 会发生什么呢?

上图是 Google 官方的一个测试结果。一个空的 ContentProvider 大约会占用 2ms 的耗时,随着数量的增加,耗时也会跟着一起增加。如果使用了50个 ContentProvider,那么将会占用接近 20ms 的耗时,这还只是空 ContentProvider 的耗时,并没有算上其中执行初始化逻辑的耗时。

所以 App Startup 其实就是为了解决这个问题诞生的。

App Startup

一句话总结:

App Startup 将所有用于初始化的 ContentProvider 合并成一个,从而使 App 的启动速度变得更快。

具体来说 App Startup 内部会创建了一个 ContentProvider,并提供了一套用于初始化的标准。其他组件只需要都按照这套标准进行实现,就可以保证在 App 启动之前成功进行初始化并可自定义先后顺序。

App Startup 的使用

1、引入依赖
dependencies {
    implementation "androidx.startup:startup-runtime:1.0.0"
}
2、定义 Initializer
// Initializes WorkManager.
class WorkManagerInitializer : Initializer<WorkManager> {
    override fun create(context: Context): WorkManager {
        val configuration = Configuration.Builder().build()
        WorkManager.initialize(context, configuration)
        return WorkManager.getInstance(context)
    }
    override fun dependencies(): List<Class<out Initializer<*>>> {
        // No dependencies on other libraries.
        return emptyList()
    }
}

如果 ExampleLogger 的初始化依赖于 WorkManager 的初始化:

// Initializes ExampleLogger.
class ExampleLoggerInitializer : Initializer<ExampleLogger> {
    override fun create(context: Context): ExampleLogger {
        // WorkManager.getInstance() is non-null only after
        // WorkManager is initialized.
        return ExampleLogger(WorkManager.getInstance(context))
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        // Defines a dependency on WorkManagerInitializer so it can be
        // initialized after WorkManager is initialized.
        return listOf(WorkManagerInitializer::class.java)
    }
}
3、注册 Provider
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- This entry makes ExampleLoggerInitializer discoverable. -->
    <meta-data  android:name="com.example.ExampleLoggerInitializer"
          android:value="androidx.startup" />
</provider>
  • 注意 android:name="com.example.ExampleLoggerInitializer" 这一条之外的内容都不要修改。
  • 被已注册的 Initializer 所依赖的 Initializer 可以不需要注册,如上的 WorkManagerInitializer
4、手动初始化(延迟初始化)

如果不想在启动时自动初始化,而是在希望的地方才进行,可使用 AppInitializer 类手动实现延迟初始化:

AppInitializer.getInstance(this).initializeComponent(WorkManagerInitializer::class.java)

但前提是要禁止自动初始化:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data android:name="com.example.ExampleLoggerInitializer"
              tools:node="remove" />
</provider>

tools:node="remove" 会在合并 AndroidManifest 时将 android:name"com.example.ExampleLoggerInitializer"meta-data 删除。

SuperApp引入

当前 IPCApplication 中也能看到不少初始化代码:

TPLog.init(IPCAppConstants.LOG_FILE_FOLDER_PATH, TPUtils.isApkInDebug(this));
......
RouterRepository.getInstance().init();
WeatherRepository.getInstance().init();
CommonProxy.getInstance().init();
DeviceProxy.getInstance().init();
CloudServiceProxy.getInstance().init();
......

SuperApp 会依赖许多 app-libs 的库,而且正在进行组件化拆分,各个库和功能组件应该会有许多有初始化需求,是可以考虑引入 App Startup 优化初始化管理的。每个库和组件实现自己的 Initializer 并在其中完成初始化,将相关逻辑保持在自己内部,更符合单一职责。然后注册到同一个 InitializationProvider 中,可以减少 IPCApplication 中的杂乱代码,有助于业务逻辑分离,让代码结构变得更加合理与清晰,新引入库或拆分出模块时也不需要去改动 IPCApplication。同时引入也是非常简单。

但个人感觉这个组件其实并不是很有用,优点是很简单,缺点则是太简单了。最主要是没有一个异步的机制,如果某个初始化是需要异步的,App Startup 无法获知异步过程的结果,也无法根据异步结果处理依赖关系,它的依赖控制更像是一个同步队列,更不能做到多个初始化同时进行,全部完成后再进行某个初始化这样的复杂控制。而且现在我们 app 的初始化复杂度也不是太高,这个组件属于可有可无,有空再搞的一个优化方案。

推荐阅读更多精彩内容