Android Jetpack 系列(七)App Startup 启动优化

1. 简介

应用启动阶段的性能至关重要。许多第三方 SDK 或库都需要在应用启动时完成初始化,而常见做法通常有两种:

**传统方式一:**在 Application 的 onCreate() 中初始化

这种方式虽然直观,但存在几个问题:

问题 1:所有初始化逻辑都堆在 onCreate() 中,难以维护;

问题 2:如果库开发者也需要自动初始化,就必须依赖宿主应用的改动;

问题 3:对库作者来说,无法保证宿主是否正确初始化。

**传统方式二:**通过 ContentProvider 自动初始化

为了让库能"自动"初始化,许多 SDK(如 WorkManager、Room、Firebase 等)会在自己的 AndroidManifest.xml 中声明一个 ContentProvider。

系统会在 Application.onCreate() 之前自动创建所有 Provider,这样库便能在应用启动前完成初始化。

但这种方式也存在问题:

问题 1:冷启动开销大,每个库一个 Provider,系统会在启动时反射并创建多个对象,增加启动时间。

问题 2:初始化顺序不可控,Android 并不保证 Provider 的执行顺序,若库之间存在依赖关系,容易出现初始化错误。

为此,Jetpack 推出了 App Startup,专门用于解决以上问题。App Startup 的优势:

  1. 简单配置即可实现自动初始化;
  2. 所有初始化逻辑共用一个 Provider,仅加载一次入口,性能更佳;
  3. 通过 dependencies() 显式声明依赖关系,保证顺序;
  4. 对应用开发者与库开发者都极为友好:
  • 应用开发者可借此简化 Application 逻辑;
  • 库开发者可实现"自动初始化",无需宿主修改。

2. 添加依赖

要在项目中使用 App Startup,需要在模块的 build.gradle.kts 或 build.gradle 文件中添加如下依赖项:

Kotlin 复制代码
dependencies {
    implementation("androidx.startup:startup-runtime:1.2.0")
}

3. 实现初始化组件

3.1. 实现 Initializer 接口

App Startup 的核心思想很简单,每个需要初始化的组件都对应一个 Initializer 类。实现 Initializer<T> 接口后,App Startup 就能在启动时自动调用它的 create() 方法完成初始化。

Kotlin 复制代码
interface Initializer<T> {
    fun create(context: Context): T
    fun dependencies(): List<Class<out Initializer<*>>>
}

方法说明:

  1. create():定义该组件的初始化逻辑。
  2. dependencies():声明依赖的其它组件初始化器,控制执行顺序。

我们在前面文章《Android Jetpack 系列(六)WorkManager 任务调度》中的【4.9.1. 默认自动初始化】中便介绍过 WorkManager 库能自动初始化正是因为它实现了这个接口,其源码如下:

java 复制代码
public final class WorkManagerInitializer implements Initializer<WorkManager> {

    private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");

    @NonNull
    @Override
    public WorkManager create(@NonNull Context context) {
        // Initialize WorkManager with the default configuration.
        Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
        WorkManager.initialize(context, new Configuration.Builder().build());
        return WorkManager.getInstance(context);
    }

    @NonNull
    @Override
    public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
        return Collections.emptyList();
    }
}

3.2. 在 Manifest 中配置初始化器

App Startup 实际上是通过一个特殊的 ContentProvider 来发现并执行这些初始化器。该 Provider 名为:

复制代码
androidx.startup.InitializationProvider

要启用自动初始化,只需在 AndroidManifest.xml 中添加 <meta-data> 配置。例如 WorkManager 的配置如下:

XML 复制代码
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge" >
        <meta-data
            android:name="androidx.work.WorkManagerInitializer"
            android:value="androidx.startup" />
    </provider>
    ......
</application>

说明:

  1. tools:node="merge" 用于多模块 Manifest 合并。
  2. android:name:配置组件初始化类的完整名称。
  3. android:value:固定为 "androidx.startup" 字符串。

3.3. 声明依赖关系

假设现在有两个库:Sdk1Sdk2,其中 Sdk2 依赖 Sdk1。可按如下方式定义:

Sdk1Initializer:

Kotlin 复制代码
class Sdk1Initializer : Initializer<Sdk1Manager> {
    override fun create(context: Context): Sdk1Manager {
        Sdk1Manager.initialize(context)
        return Sdk1Manager.getInstance()
    }

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

Sdk2Initializer:

Kotlin 复制代码
class Sdk2Initializer : Initializer<Sdk2Manager> {
    override fun create(context: Context): Sdk2Manager {
        Sdk2Manager.initialize(context)
        return Sdk2Manager.getInstance()
    }

    override fun dependencies(): List<Class<out Initializer<*>?>?> {
        val dependencies: MutableList<Class<out Initializer<*>?>?> = ArrayList()
        dependencies.add(Sdk1Initializer::class.java)
        return dependencies
    }
}

Manifest 配置:

XML 复制代码
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        
        <meta-data
            android:name="com.zyx.app.startup.Sdk2Initializer"
            android:value="androidx.startup" />
    </provider>
    ......
</application>

说明:

  1. Sdk2Initializer 的 dependencies() 已声明了依赖关系,App Startup 会自动识别执行顺序(先 Sdk1 再 Sdk2)。
  2. 即使 Sdk1Initializer 未出现在任何 <meta-data> 中,也会被自动加载。
  3. 若两者都配置了 <meta-data>,不会重复初始化,因为 App Startup 内部有缓存与拓扑排序机制,能确保每个组件只执行一次。

4. 取消组件自动初始化

某些场景下,我们可能不希望在应用启动时立即初始化所有组件。

例如,有些 SDK 在初始化时会访问敏感数据或申请权限,通常应在用户同意隐私政策后再执行。

此时可以禁用自动初始化并改为手动初始化。

4.1 禁用单个组件

可通过移除该组件的 <meta-data> 项来禁用自动初始化,例如:

XML 复制代码
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        
        <meta-data
            android:name="com.zyx.app.startup.Sdk2Initializer"
            tools:node="remove" />
    </provider>
    ......
</application>

若想禁用所有组件的自动初始化,可移除整个 Provider:

XML 复制代码
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="remove">
    </provider>
    ......
</application>

说明:

  1. tools:node="remove" 会在 Manifest 合并阶段移除对应节点。
  2. 禁用组件自动初始化后,系统不会再递归初始化其依赖。

4.2. 手动初始化组件

在需要时,可使用以下方式手动触发初始化:

Kotlin 复制代码
AppInitializer.getInstance(context)
    .initializeComponent(Sdk2Initializer::class.java)

此调用会自动递归初始化其依赖(例如 Sdk1),无需手动顺序控制。

5. Lint 检查

App Startup 提供了内置的 Lint 规则,可帮助检测初始化配置是否正确:

Kotlin 复制代码
./gradlew :app:lintDebug

Lint 会检测并提示以下问题:

  1. 未注册的初始化器;
  2. 依赖循环;
  3. 无效的 <meta-data>;
  4. 未使用的初始化器。

6. 总结

App Startup 为 Android 提供了一种统一、简洁且高效的组件初始化方案。

它不仅减少了启动阶段的性能开销,还让依赖关系更清晰、初始化逻辑更可控。

无论是应用开发者还是 SDK 作者,都能从中获益。

更多详细的 App Startup 介绍,请访问 Android 开发者官网

相关推荐
冰语竹2 分钟前
滚动视图HorizontalScrollView和ScrollView
android
studyForMokey18 分钟前
【跨端技术ReactNative】JavaScript学习
android·javascript·学习·react native·react.js
Be for thing22 分钟前
Android 充电 & BMS 电池管理系统原理与测试实战(手机 / 手表通用)
android·学习·智能手机
robotx1 小时前
安卓15开机动画BootAnimation启动源码简单分析
android
Hi~晴天大圣1 小时前
MySQL中JSON 格式字段里某个值修改
android·mysql·json
BoomHe1 小时前
Kotlin shareIn 和 stateIn 使用场景
android·kotlin·android jetpack
张雨zy1 小时前
Vue 的 v-if 与 v-show,Android 的 GONE 与 INVISIBLE
android·前端·vue.js
飘逸飘逸2 小时前
Autojs进阶-插件更新记录
android·javascript
Be for thing2 小时前
Android 传感器硬件原理 + 功耗测试与异常定位实战(手表 / IoT / 手机通用)
android·学习·智能手机
阿拉斯攀登2 小时前
第 8 篇 RK 平台安卓驱动实战 1:GPIO 输入输出驱动,从内核到 App 全流程打通
android·驱动开发·rk3568·瑞芯微·rk安卓驱动