startup 和 WorkManager

111 阅读3分钟

如何用 App Startup + WorkManager 解决冷启动问题。

📱 ​场景模拟

假设你的电商APP需要初始化以下组件:

  1. 关键路径​(必须立即初始化)
    • 数据库(Room)、用户登录状态(Auth)
  2. 非关键路径​(可延迟)
    • 支付SDK(Alipay)、数据统计(Firebase Analytics)、广告SDK(AdMob)

🛠️ ​解决方案代码

​1. 配置依赖(build.gradle)​

gradle复制// app/build.gradle
dependencies {
    // 必须组件
    implementation "androidx.room:room-runtime:2.5.0"
    implementation "com.auth0:auth0:2.0.0"

    // 延迟初始化组件
    implementation "com.google.firebase:firebase-analytics-ktx:21.3.0"
    implementation "com.google.android.gms:play-services-ads:22.4.0"
    implementation "com.alipay.sdk:alipay:15.8.11"

    // Jetpack工具
    implementation "androidx.startup:startup-runtime:1.1.1"
    implementation "androidx.work:work-runtime-ktx:2.8.1"
}

​2. 关键路径初始化(App Startup)​

​**(1) 定义数据库初始化器
kotlin复制// DatabaseInitializer.kt
class DatabaseInitializer : Initializer<AppDatabase> {
    override fun create(context: Context): AppDatabase {
        // 👉 主线程初始化(Room会自己开IO线程)
        return Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
            .fallbackToDestructiveMigration()
            .build()
    }

    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
​**(2) 定义认证初始化器(依赖数据库)​**
kotlin复制// AuthInitializer.kt
class AuthInitializer : Initializer<AuthManager> {
    override fun create(context: Context): AuthManager {
        // 👉 确保数据库已初始化
        val db = AppInitializer.getInstance(context)
            .initializeComponent(DatabaseInitializer::class.java)
        // 👇 模拟3秒耗时操作(⚠️ 会阻塞主线程!仅用于测试)
        Thread.sleep(2000)        
        return AuthManager.getInstance(db.userDao())
    }

    override fun dependencies(): List<Class<out Initializer<*>>> = 
        listOf(DatabaseInitializer::class.java)
}
​**(3) 注册到AndroidManifest.xml**
xml复制<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false">
    <meta-data
        android:name="com.example.initializer.DatabaseInitializer"
        android:value="androidx.startup" />
    <meta-data
        android:name="com.example.initializer.AuthInitializer"
        android:value="androidx.startup" />
</provider>

​3. 非关键路径延迟初始化(WorkManager)​

​**(1) 定义后台初始化Worker**
kotlin复制// DeferredInitializerWorker.kt
class DeferredInitializerWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        // 👉 在后台线程执行耗时初始化
        return try {
            // 案例1: 支付SDK(需要网络)
            AlipaySDK.init(applicationContext)

            // 案例2: Firebase(可能崩溃)
            FirebaseAnalytics.getInstance(applicationContext)

            // 案例3: 广告SDK(可重试)
            MobileAds.initialize(applicationContext)

            Result.success()
        } catch (e: Exception) {
            // 失败后自动重试(WorkManager默认策略)
            Result.retry()
        }
    }
}
​**(2) 触发延迟初始化**
kotlin复制// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 当用户进入主界面后,再启动非关键初始化
        val initRequest = OneTimeWorkRequestBuilder<DeferredInitializerWorker>()
            .setConstraints(
                Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED)
                    .setRequiresBatteryNotLow(true) // 低电量不执行
                    .build()
            )
            .setInitialDelay(5, TimeUnit.SECONDS) // 延迟5秒执行
            .build()

        //        Q: 如何避免重复任务?
        //        A: 使用 UniqueWorkRequest 确保同一任务只存在一个实例:        
        WorkManager.getInstance(context).enqueueUniqueWork("task_name", ExistingWorkPolicy.KEEP, initRequest)
    }
}

⚡ ​防御效果对比

场景

传统方式

优化后方案

主线程阻塞

所有SDK在主线程串行初始化(3秒)

仅关键路径在主线程执行(0.5秒)

支付SDK失败

直接崩溃

自动重试3次后放弃,不影响主流程

无网络时初始化

统计SDK初始化失败

等待网络恢复后自动执行

代码维护

所有初始化堆在Application

职责分离,每个组件独立管理

📊 ​性能数据模拟(Android Profiler)​

text复制| 初始化阶段          | 耗时  |
|---------------------|-------|
| Application.onCreate | 480ms |
| 首屏渲染完成        | 900ms |
| 广告SDK完成         | 后台延迟执行(不影响用户体验) |

🔍 ​关键技巧

  1. 依赖隔离

    kotlin复制// 错误示例:直接交叉初始化
    Firebase.init() // 依赖用户ID,但用户ID需要从数据库读取
    

    ✅ 正确做法:通过 dependencies() 声明依赖关系。

  2. WorkManager重试策略

    kotlin复制.setBackoffCriteria(
        BackoffPolicy.LINEAR,
        OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
        TimeUnit.MILLISECONDS
    )
    
  3. 调试工具
    使用 App Startup 的日志功能检查初始化顺序:

    bash复制adb logcat -s AppInitializer
    

🚀 ​总结

  • 立即初始化​(App Startup):数据库、用户认证等启动必备组件
  • 延迟初始化​(WorkManager):支付、统计、广告等非关键组件
  • 防御核心:通过职责分离 + 自动重试,实现 ​启动加速 和 ​异常拦截