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 开发者官网

相关推荐
普通网友1 小时前
Android Jetpack从入门到精通,干货满满
android·android jetpack
嫩嫩的猿1 小时前
android jetpack compose Model对象更新变量 UI不更新、不刷新问题
android·ui·android jetpack
普通网友1 小时前
Android Jetpack 之 LifeCycle 组件_android 自定义view lifecycle
android·gitee·android jetpack
_codemonster2 小时前
数据库字符集编码问题
android·数据库·oracle
Pika8 小时前
深入浅出Compose HitTest 机制
android·android jetpack
资深web全栈开发8 小时前
CoI - 组合优于继承:解耦的艺术
android·java·开发语言
冬奇Lab9 小时前
WMS进阶:多窗口模式与显示管理深度解析
android·源码阅读
加农炮手Jinx10 小时前
Flutter for OpenHarmony:web_socket_channel 全平台 WebSocket 通信标准库,从原理到鸿蒙实战(3000字深度解析)
android·前端·网络·websocket·flutter·华为·harmonyos
王码码203510 小时前
Flutter for OpenHarmony:web_socket 纯 Dart 标准 WebSocket 客户端(跨平台兼容性之王) 深度解析与鸿蒙
android·前端·websocket·网络协议·flutter·华为·harmonyos