学习Android启动初始化 App StartUp

微生智刚
2023-12-01

StartUp是为了App的启动提供的一套简单、高效的初始化方案。

ContentProvider中初始化

在项目中会需要用到很多的第三方库,而很多第三方库都提供了显示的调用初始化接口,需要在Application中进行初始化,并获取到Application的Context。

于是乎,Application中的代码就可能会变成这个样子:

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        A.initialize(this)
        B.initialize(this)
        C.initialize(this)
        ...
    }
    ...
}

随着引入的第三方库越来越多,Application中的代码也是越来越庞大。

于是乎,有些更加聪明的库设计者,他们想到了一种非常巧妙的办法来避免显示的调用初始化接口,而是可以自动调用初始化接口,这种办法就是借助ContentProvider。

ContentProvider作为Android四大组件之一,其主要作用是跨应用程序共享数据。

然而这些第三方库并没有打算使用ContentProvider来跨应用程序共享数据,只是准备使用它拿到Context进行初始化而已。在APP的启动流程中,有一步就是要执行到程序中所有注册过的ContentProvider的onCreate方法,所以这些第三方库的初始化就默默自动完成了。

这种设计方式可以将库的用法进一步简化,不需要主动去调用初始化接口,而是将这个工作在背后悄悄自动完成了,给集成库的开发者们带来了很大的便利。很多库都用到了这种方法,比如Facebook,Firebase。

但是呢,看上去如此巧妙的技术方案,有一个很大的缺点就是,ContentProvider会增加许多额外的耗时。因为不同的库就定义了不同的ContentProvider类,多了这么多ContentProvider,ContentProvider作为四大组件之一,启动也是耗时的,自然也就增加App启动消耗的时间了。

这时候就需要App Startup来对此情况进行优化了。

App Startup

首先来看一下官网对于Startup的简介:

The App Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use App Startup to streamline startup sequences and explicitly set the order of initialization.
Instead of defining separate content providers for each component you need to initialize, App Startup allows you to define component initializers that share a single content provider. This can significantly improve app startup time.

主要说到了两点特性:

  • 明确设置初始化顺序
  • 共享单个ContentProvider

其实,App Startup内部也是创建了一个ContentProvider,并提供了一套用于初始化的标准。然后对于其他第三方库来说,就不需要再自己创建ContentProvider了,都按Startup这套标准进行实现就行了。同时Startup还提供了可以设置初始化顺序。

App Startup使用

首先,引入库:

implementation 'androidx.startup:startup-runtime:1.1.1'

然后定义一个用于执行初始化的Initializer,并实现App Startup库的Initializer接口:

class ARouterInitializer : Initializer<String> {
    override fun create(context: Context): String {
        ARouter.init(context.applicationContext as Application)
        return "ARouterInit"
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}

实现Initializer接口要求重写两个方法,在create()方法中可以进行初始化操作,这里以ARouter发初始化为例。
dependencies()方法表示,当前的初始化是否还依赖于其他的Initializer,如果有的话,就在这里进行配置,App Startup会保证先初始化依赖的Initializer,然后才会初始化当前,这样就可以设置初始化顺序了。当然,绝大多数的情况下,初始化操作都是不会依赖于其他Initializer的,所以通常直接返回一个emptyList()就可以了。

最后,在AndroidManifest.xml中进行配置,这里需要严格按照Startup的配置规范:

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

只有meta-data中的android:name部分需要指定成自定义的Initializer的全路径类名,其他部分都是不能修改的,否则App Startup库可能会无法正常工作。
tools:node="merge"标签就是用来合并所有申明了InitializationProvider的ContentProvider。

延迟初始化

如果不希望初始化在应用启动的时候自动初始化,App Startup也是提供了手动调用初始化的方法。

        <!-- 禁用所有InitializationProvider组件初始化 -->
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            tools:node="remove" />
        <!-- 禁用单个InitializationProvider组件初始化 -->
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data
                android:name="com.example.base.ARouterInitializer"
                tools:node="remove" />
        </provider>

使用tools:node="remove"标签,这个标签用于告诉manifest merger tool,在最后打包成APK时,将所有该名称的节点全部删除。可以禁用所有InitializationProvider组件初始化,也可以禁用单个InitializationProvider组件初始化。

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

然后再手动调用App Startup提供的初始化方法。

 类似资料: