跳到主要内容

启动优化

问题

Android 应用的启动过程是怎样的?如何优化冷启动时间?

答案

三种启动方式

类型说明耗时
冷启动进程不存在,从 fork 进程开始最长
温启动进程存在但 Activity 被销毁中等
热启动Activity 在内存中,直接 resume最短

冷启动全流程

优化策略

1. Application.onCreate 优化

@HiltAndroidApp
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// ❌ 同步初始化所有 SDK
// initCrashlytics()
// initAnalytics()
// initPush()

// ✅ 按优先级分级初始化
// 必须同步:Crashlytics
initCrashlytics()

// 延迟到空闲时:Analytics、Push
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post {
// IdleHandler 在主线程空闲时执行
Looper.myQueue().addIdleHandler {
initAnalytics()
initPush()
false // 执行一次后移除
}
}
}
}

2. 使用 App Startup 库

// 声明式初始化,替代 ContentProvider 自动初始化
class AnalyticsInitializer : Initializer<Analytics> {
override fun create(context: Context): Analytics {
return Analytics.init(context)
}

override fun dependencies(): List<Class<out Initializer<*>>> {
return listOf(CrashInitializer::class.java) // 依赖关系
}
}

3. 布局优化

// 使用 ViewStub 延迟加载非首屏内容
// 首屏只加载核心 UI,其他用 ViewStub

// Compose 场景下:减少首帧的 Composable 数量
@Composable
fun HomeScreen() {
// 首帧只显示骨架屏
var loaded by remember { mutableStateOf(false) }

if (loaded) {
FullContent()
} else {
Skeleton()
}

LaunchedEffect(Unit) {
loaded = true
}
}

4. Baseline Profiles(基线配置文件)

// 预编译热路径代码,减少 JIT 开销
@ExperimentalBaselineProfilesApi
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()

@Test
fun generateBaselineProfile() = rule.collect("com.example.app") {
pressHome()
startActivityAndWait()
// 模拟启动关键路径
}
}

测量启动时间

# adb 测量
adb shell am start-activity -W com.example/.MainActivity
# TotalTime: 冷启动总耗时

# Macrobenchmark 自动化测量
@LargeTest
class StartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()

@Test
fun startup() = benchmarkRule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) {
pressHome()
startActivityAndWait()
}
}

常见面试问题

Q1: 如何将冷启动优化到 500ms 以内?

答案

  1. 减少 Application.onCreate 耗时:异步初始化、按需加载
  2. 减少首帧布局复杂度:扁平化布局、ViewStub
  3. 使用 Baseline Profiles:预编译热路径
  4. 避免主线程 IO:使用 StrictMode 检测
  5. 使用 SplashScreen API:利用系统窗口快速展示品牌页

Q2: App Startup 库的原理?

答案

传统方式中,许多库通过自己的 ContentProvider 自动初始化,每个 CP 都有开销。App Startup 合并所有初始化到一个 InitializationProvider 中,按依赖关系图有序执行,减少 ContentProvider 数量。

相关链接