Android app启动速度优化

目录


一、启动类型概述

1.1 三种启动类型

类型 定义 特点
冷启动 进程从 0 开始创建 耗时最长,重点优化
温启动 进程已存在,Activity 重建 耗时中等
热启动 Activity 从后台切前台 耗时最短

1.2 冷启动完整流程

scss 复制代码
用户点击 Launcher
    ↓
ActivityManagerService 处理 Intent
    ↓
Zygote 进程 fork 新进程
    ↓
Application.attachBaseContext()
    ↓
Application.onCreate()
    ↓
Activity.onCreate()
    ↓
Activity.onStart()
    ↓
Activity.onResume()
    ↓
Choreographer 第一次回调
    ↓
首帧绘制完成(用户可见)

二、指标体系建立

2.1 核心指标

指标 定义 优秀值 合格值
冷启动总耗时 点击到首帧 < 800ms < 1500ms
Application 初始化 attachBaseContext 到 onCreate 结束 < 200ms < 400ms
Activity 创建 onCreate 到 onResume 结束 < 300ms < 500ms
首帧绘制 onResume 到首帧 < 100ms < 200ms
完全可交互 核心数据加载完成 < 1200ms < 2500ms

2.2 线上分位统计

分位 目标值
P50 < 800ms
P90 < 1200ms
P95 < 1500ms
P99 < 2500ms

2.3 指标监控维度

  • 设备品牌、型号
  • Android 版本
  • 应用版本
  • RAM 大小
  • 网络环境

三、各阶段优化方案

3.1 进程创建阶段

优化点

优化项 方案
类加载优化 主 dex 只放启动必须的类
MultiDex 主 dex 精准配置
JIT 禁用 Android N+ 可禁用 JIT 加速启动
避免反射 启动期避免使用反射

代码示例:禁用 JIT

xml 复制代码
<!-- AndroidManifest.xml -->
<application
    android:vmSafeMode="true">
</application>

3.2 Application 初始化阶段

优化策略

  1. 区分进程:只在主进程初始化
  2. 必要/非必要分离:必要任务同步,非必要异步
  3. 延迟初始化:使用 IdleHandler 在空闲时执行
  4. 任务启动器:用专门的任务启动器管理

代码示例:进程区分

kotlin 复制代码
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        if (isMainProcess()) {
            initEssential()
            delayInitNonEssential()
        }
    }
    
    private fun isMainProcess(): Boolean {
        val processName = getProcessName(this)
        return processName == packageName
    }
    
    private fun getProcessName(context: Context): String {
        val am = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
        return am?.runningAppProcesses?.find { it.pid == Process.myPid() }?.processName.orEmpty()
    }
}

代码示例:IdleHandler 延迟初始化

kotlin 复制代码
Handler(Looper.getMainLooper()).postIdle {
    initPush()
    initMap()
    initAnalytics()
    false // 只执行一次
}

3.3 Activity 创建阶段

优化策略

  1. 布局优化:减少层级、使用 merge/include
  2. 异步加载:AsyncLayoutInflater 异步 inflate
  3. ViewStub:延迟加载非必须 View
  4. 数据预加载:提前加载首页数据

代码示例:布局优化

xml 复制代码
<!-- 使用 merge 减少层级 -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <include layout="@layout/header" />
    <include layout="@layout/content" />
</merge>
xml 复制代码
<!-- 使用 ViewStub 延迟加载 -->
<ViewStub
    android:id="@+id/stub_detail"
    android:layout="@layout/layout_detail"
    android:inflatedId="@+id/fl_detail" />

代码示例:异步加载布局

kotlin 复制代码
AsyncLayoutInflater(this).inflate(
    R.layout.activity_main,
    null
) { view, _, _ ->
    setContentView(view)
    initViews(view)
}

3.4 首帧绘制阶段

优化策略

  1. SplashScreen API:使用官方启动页
  2. 消除黑白屏:设置 windowBackground
  3. 过度绘制:减少不必要的背景
  4. 简化首屏:首屏只显示必要内容

代码示例:SplashScreen API

xml 复制代码
<!-- values/themes.xml -->
<style name="Theme.Launcher" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">@color/white</item>
    <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
    <item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
kotlin 复制代码
// Activity 中
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    val splashScreen = installSplashScreen()
    splashScreen.setKeepOnScreenCondition { 
        !viewModel.isDataReady.value 
    }
}

四、MultiDex 优化

4.1 主 Dex 配置方式

AGP 8.0+

kotlin 复制代码
// app/build.gradle.kts
android {
    defaultConfig {
        multiDexEnabled = true
    }
    
    buildTypes {
        release {
            multiDexKeepProguard = file("multidex-keep.pro")
        }
    }
}

AGP 7.x 及以下

groovy 复制代码
// app/build.gradle
android {
    defaultConfig {
        multiDexEnabled true
    }
    
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-keep.pro')
        }
    }
}

4.2 multidex-keep.pro 配置

proguard 复制代码
# 核心启动类
-keep class com.yourapp.MyApplication { *; }
-keep class com.yourapp.MainActivity { *; }
-keep class com.yourapp.SplashActivity { *; }
-keep class com.yourapp.base.BaseActivity { *; }

# 基础工具类
-keep class com.yourapp.utils.AppUtils { *; }
-keep class com.yourapp.constants.* { *; }

# 启动必须的第三方 SDK
-keep class com.tencent.bugly.** { *; }
-keep class com.umeng.analytics.** { *; }

# ContentProvider
-keep class com.yourapp.provider.** { *; }

# 保留注解
-keep class androidx.annotation.Keep
-keep @androidx.annotation.Keep class * {*;}

4.3 验证主 Dex 配置

方法 1:APK Analyzer

  1. Android Studio → Build → Analyze APK
  2. 选择 APK 文件
  3. 查看 classes.dex 的内容

方法 2:命令行

bash 复制代码
# 使用 build-tools 中的 dexdump
DEXDUMP=$ANDROID_HOME/build-tools/34.0.0/dexdump
unzip -p app-release.apk classes.dex > classes.dex
$DEXDUMP classes.dex | grep "Class descriptor"

4.4 常见问题

问题 解决方案
ClassNotFoundException multidex-keep.pro 中添加该类
主 Dex 方法超限 进一步精简,延迟初始化更多 SDK
ContentProvider 初始化失败 确保 Provider 在主 Dex 中

五、任务启动器

5.1 架构设计

scss 复制代码
TaskLauncher (入口)
    ↓
TaskManager (任务管理)
    ↓
TaskDispatcher (任务分发)
    ├─ 主线程 Executor
    ├─ IO 线程池
    ├─ CPU 密集线程池
    └─ 单线程 Executor

5.2 核心代码

Task.kt - 任务定义

kotlin 复制代码
package com.yourapp.launcher

import android.os.Process
import android.os.SystemClock
import androidx.annotation.IntRange

const val PRIORITY_LOWEST = -20
const val PRIORITY_LOW = -10
const val PRIORITY_DEFAULT = 0
const val PRIORITY_HIGH = 10
const val PRIORITY_HIGHEST = 20

enum class ThreadType {
    MAIN, IO, CPU, SINGLE
}

abstract class Task {
    open val name: String = this::class.java.simpleName
    open val priority: Int = PRIORITY_DEFAULT
    open val threadType: ThreadType = ThreadType.IO
    open val needWait: Boolean = false
    open val dependencies: List<Class<out Task>> = emptyList()
    open val timeoutMillis: Long = 3000L
    open val isEnabled: Boolean = true
    
    internal val isFinished = java.util.concurrent.atomic.AtomicBoolean(false)
    internal val isRunning = java.util.concurrent.atomic.AtomicBoolean(false)
    internal val latch = java.util.concurrent.CountDownLatch(1)
    
    abstract fun run()
    
    open fun onStart() {}
    open fun onSuccess() {}
    open fun onError(e: Throwable) {}
    open fun onTimeout() {}
    
    internal fun execute() {
        if (!isEnabled) {
            finish()
            return
        }
        if (!isRunning.compareAndSet(false, true)) return
        
        onStart()
        val startTime = SystemClock.uptimeMillis()
        
        try {
            when (threadType) {
                ThreadType.MAIN -> Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT)
                ThreadType.IO -> Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
                ThreadType.CPU -> Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT)
                ThreadType.SINGLE -> Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
            }
            run()
            onSuccess()
        } catch (e: Throwable) {
            onError(e)
            TaskLauncher.monitor?.onTaskError(this, e)
        } finally {
            val endTime = SystemClock.uptimeMillis()
            TaskLauncher.monitor?.onTaskComplete(this, endTime - startTime)
            finish()
        }
    }
    
    internal fun waitFinish() {
        try {
            if (!latch.await(timeoutMillis, java.util.concurrent.TimeUnit.MILLISECONDS)) {
                onTimeout()
                TaskLauncher.monitor?.onTaskTimeout(this)
            }
        } catch (e: InterruptedException) {
            Thread.currentThread().interrupt()
        }
    }
    
    private fun finish() {
        isFinished.set(true)
        latch.countDown()
    }
}

// DSL 快速创建
inline fun createTask(
    name: String,
    priority: Int = PRIORITY_DEFAULT,
    threadType: ThreadType = ThreadType.IO,
    needWait: Boolean = false,
    dependencies: List<Class<out Task>> = emptyList(),
    crossinline block: () -> Unit
): Task = object : Task() {
    override val name: String = name
    override val priority: Int = priority
    override val threadType: ThreadType = threadType
    override val needWait: Boolean = needWait
    override val dependencies: List<Class<out Task>> = dependencies
    override fun run() = block()
}

TaskDispatcher.kt - 任务分发器

kotlin 复制代码
package com.yourapp.launcher

import android.os.Handler
import android.os.Looper
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicInteger

internal class TaskDispatcher {
    companion object {
        private const val CPU_COUNT = 4
        private const val CORE_POOL_SIZE = CPU_COUNT + 1
        private const val MAX_POOL_SIZE = CPU_COUNT * 2 + 1
        private const val KEEP_ALIVE_SECONDS = 30L
    }
    
    private val mainHandler = Handler(Looper.getMainLooper())
    
    private val ioExecutor: ThreadPoolExecutor = ThreadPoolExecutor(
        CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
        LinkedBlockingQueue(), TaskThreadFactory("launcher-io-"),
        ThreadPoolExecutor.CallerRunsPolicy
    )
    
    private val cpuExecutor: ThreadPoolExecutor = ThreadPoolExecutor(
        CPU_COUNT, CPU_COUNT, 0L, TimeUnit.MILLISECONDS,
        LinkedBlockingQueue(), TaskThreadFactory("launcher-cpu-"),
        ThreadPoolExecutor.CallerRunsPolicy
    )
    
    private val singleExecutor: ExecutorService = Executors.newSingleThreadExecutor(
        TaskThreadFactory("launcher-single-")
    )
    
    init {
        ioExecutor.allowCoreThreadTimeOut(true)
    }
    
    fun dispatch(task: Task) {
        when (task.threadType) {
            ThreadType.MAIN -> {
                if (isMainThread()) task.execute()
                else mainHandler.post { task.execute() }
            }
            ThreadType.IO -> ioExecutor.execute { task.execute() }
            ThreadType.CPU -> cpuExecutor.execute { task.execute() }
            ThreadType.SINGLE -> singleExecutor.execute { task.execute() }
        }
    }
    
    fun postToMain(runnable: Runnable) = mainHandler.post(runnable)
    fun isMainThread(): Boolean = Looper.myLooper() == Looper.getMainLooper()
    fun shutdown() {
        ioExecutor.shutdown()
        cpuExecutor.shutdown()
        singleExecutor.shutdown()
    }
    
    private class TaskThreadFactory(private val namePrefix: String) : ThreadFactory {
        private val threadNumber = AtomicInteger(1)
        override fun newThread(r: Runnable): Thread {
            return Thread(r, namePrefix + threadNumber.getAndIncrement()).apply {
                isDaemon = true
            }
        }
    }
}

TaskManager.kt - 任务管理器

kotlin 复制代码
package com.yourapp.launcher

import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.LinkedBlockingQueue

internal class TaskManager(private val dispatcher: TaskDispatcher) {
    private val allTasks = CopyOnWriteArrayList<Task>()
    private val taskMap = ConcurrentHashMap<Class<out Task>, Task>()
    private val inDegreeMap = ConcurrentHashMap<Class<out Task>, Int>()
    private val adjacencyMap = ConcurrentHashMap<Class<out Task>, MutableList<Class<out Task>>>()
    private val zeroDegreeQueue = LinkedBlockingQueue<Task>()
    private val needWaitTasks = CopyOnWriteArrayList<Task>()
    
    fun addTask(task: Task) {
        if (!task.isEnabled || taskMap.containsKey(task::class.java)) return
        
        allTasks.add(task)
        taskMap[task::class.java] = task
        
        if (task.needWait) needWaitTasks.add(task)
        
        inDegreeMap[task::class.java] = task.dependencies.size
        
        if (task.dependencies.isEmpty()) {
            zeroDegreeQueue.offer(task)
        } else {
            task.dependencies.forEach { depClass ->
                adjacencyMap.getOrPut(depClass) { mutableListOf() }.add(task::class.java)
            }
        }
    }
    
    fun addTasks(tasks: List<Task>) {
        tasks.forEach { addTask(it) }
        val sorted = zeroDegreeQueue.sortedByDescending { it.priority }
        zeroDegreeQueue.clear()
        zeroDegreeQueue.addAll(sorted)
    }
    
    fun start() {
        while (zeroDegreeQueue.isNotEmpty()) {
            val task = zeroDegreeQueue.poll() ?: break
            dispatchTask(task)
        }
    }
    
    fun await() = needWaitTasks.forEach { it.waitFinish() }
    
    fun onTaskFinish(task: Task) {
        val dependents = adjacencyMap[task::class.java] ?: return
        
        dependents.forEach { dependentClass ->
            val inDegree = inDegreeMap[dependentClass] ?: return@forEach
            val newInDegree = inDegree - 1
            inDegreeMap[dependentClass] = newInDegree
            
            if (newInDegree == 0) {
                taskMap[dependentClass]?.let { dependentTask ->
                    zeroDegreeQueue.offer(dependentTask)
                    dispatchTask(dependentTask)
                }
            }
        }
    }
    
    private fun dispatchTask(task: Task) = dispatcher.dispatch(task)
    fun getAllTasks(): List<Task> = allTasks.toList()
}

TaskLauncher.kt - 启动器入口

kotlin 复制代码
package com.yourapp.launcher

import android.content.Context
import android.os.SystemClock

object TaskLauncher {
    interface Monitor {
        fun onStart()
        fun onTaskStart(task: Task)
        fun onTaskComplete(task: Task, duration: Long)
        fun onTaskError(task: Task, error: Throwable)
        fun onTaskTimeout(task: Task)
        fun onFinish(totalDuration: Long)
    }
    
    private class EmptyMonitor : Monitor {
        override fun onStart() {}
        override fun onTaskStart(task: Task) {}
        override fun onTaskComplete(task: Task, duration: Long) {}
        override fun onTaskError(task: Task, error: Throwable) {}
        override fun onTaskTimeout(task: Task) {}
        override fun onFinish(totalDuration: Long) {}
    }
    
    var monitor: Monitor = EmptyMonitor()
        private set
    
    private var isInitialized = false
    private lateinit var dispatcher: TaskDispatcher
    private lateinit var manager: TaskManager
    private var startTime: Long = 0
    
    fun init(context: Context) {
        if (isInitialized) return
        dispatcher = TaskDispatcher()
        manager = TaskManager(dispatcher)
        isInitialized = true
    }
    
    fun setMonitor(monitor: Monitor) {
        this.monitor = monitor
    }
    
    fun addTask(task: Task): TaskLauncher {
        checkInitialized()
        manager.addTask(task)
        return this
    }
    
    fun addTasks(tasks: List<Task>): TaskLauncher {
        checkInitialized()
        manager.addTasks(tasks)
        return this
    }
    
    fun start() {
        checkInitialized()
        startTime = SystemClock.uptimeMillis()
        monitor.onStart()
        manager.start()
    }
    
    fun await() {
        checkInitialized()
        manager.await()
        val totalDuration = SystemClock.uptimeMillis() - startTime
        monitor.onFinish(totalDuration)
    }
    
    fun startAndAwait() {
        start()
        await()
    }
    
    fun shutdown() {
        if (::dispatcher.isInitialized) dispatcher.shutdown()
        isInitialized = false
    }
    
    private fun checkInitialized() {
        check(isInitialized) { "TaskLauncher not initialized, call init() first" }
    }
}

5.3 实际使用示例

定义初始化任务

kotlin 复制代码
package com.yourapp.init

import android.content.Context
import com.yourapp.launcher.*

class InitBuglyTask(private val context: Context) : Task() {
    override val name: String = "InitBugly"
    override val priority: Int = PRIORITY_HIGHEST
    override val threadType: ThreadType = ThreadType.MAIN
    override val needWait: Boolean = true
    
    override fun run() {
        // Bugly.init(context, "YOUR_APP_ID", BuildConfig.DEBUG)
        Thread.sleep(50)
    }
}

class InitNetworkTask : Task() {
    override val name: String = "InitNetwork"
    override val priority: Int = PRIORITY_HIGH
    override val threadType: ThreadType = ThreadType.IO
    override val needWait: Boolean = false
    
    override fun run() {
        Thread.sleep(100)
    }
}

class InitDatabaseTask : Task() {
    override val name: String = "InitDatabase"
    override val priority: Int = PRIORITY_HIGH
    override val threadType: ThreadType = ThreadType.IO
    override val needWait: Boolean = false
    
    override fun run() {
        Thread.sleep(120)
    }
}

class PreCacheTask : Task() {
    override val name: String = "PreCache"
    override val priority: Int = PRIORITY_HIGH
    override val threadType: ThreadType = ThreadType.IO
    override val needWait: Boolean = false
    override val dependencies: List<Class<out Task>> = listOf(InitDatabaseTask::class.java)
    
    override fun run() {
        Thread.sleep(80)
    }
}

Application 中集成

kotlin 复制代码
package com.yourapp

import android.app.Application
import android.os.Handler
import android.os.Looper
import com.yourapp.init.*
import com.yourapp.launcher.*

class MyApplication : Application() {
    
    private val handler = Handler(Looper.getMainLooper())
    
    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        TaskLauncher.init(this)
        TaskLauncher.setMonitor(LaunchMonitor)
    }
    
    override fun onCreate() {
        super.onCreate()
        
        if (isMainProcess()) {
            val criticalTasks = listOf(
                InitBuglyTask(this),
                InitConfigTask(this)
            )
            
            val asyncTasks = listOf(
                InitNetworkTask(),
                InitImageLoaderTask(),
                InitDatabaseTask(),
                InitPushTask(),
                InitMapTask(),
                PreCacheTask()
            )
            
            val delayTasks = listOf(
                InitAnalyticsTask(),
                InitShareTask(),
                InitPayTask()
            )
            
            TaskLauncher
                .addTasks(criticalTasks)
                .addTasks(asyncTasks)
                .startAndAwait()
            
            handler.post {
                TaskLauncher.addTasks(delayTasks).start()
            }
        }
    }
    
    private fun isMainProcess(): Boolean {
        val processName = getProcessName(this)
        return processName == packageName
    }
    
    private fun getProcessName(context: Context): String {
        val am = context.getSystemService(Context.ACTIVITY_SERVICE) as? android.app.ActivityManager
        return am?.runningAppProcesses?.find { it.pid == android.os.Process.myPid() }?.processName.orEmpty()
    }
}

监控器实现

kotlin 复制代码
package com.yourapp

import android.util.Log
import com.yourapp.launcher.Task
import com.yourapp.launcher.TaskLauncher

object LaunchMonitor : TaskLauncher.Monitor {
    private const val TAG = "LaunchMonitor"
    private val taskTimes = mutableMapOf<String, Long>()
    
    override fun onStart() {
        Log.d(TAG, "========== 任务启动器开始 ==========")
    }
    
    override fun onTaskStart(task: Task) {
        Log.d(TAG, "任务开始: ${task.name}")
    }
    
    override fun onTaskComplete(task: Task, duration: Long) {
        Log.d(TAG, "任务完成: ${task.name}, 耗时: ${duration}ms")
        taskTimes[task.name] = duration
    }
    
    override fun onTaskError(task: Task, error: Throwable) {
        Log.e(TAG, "任务错误: ${task.name}", error)
    }
    
    override fun onTaskTimeout(task: Task) {
        Log.w(TAG, "任务超时: ${task.name}")
    }
    
    override fun onFinish(totalDuration: Long) {
        Log.d(TAG, "========== 任务启动器结束 ==========")
        Log.d(TAG, "总耗时: ${totalDuration}ms")
        
        Log.d(TAG, "任务耗时排行榜:")
        taskTimes.entries
            .sortedByDescending { it.value }
            .forEach { (name, time) ->
                Log.d(TAG, "  $name: ${time}ms")
            }
    }
}

5.4 优化效果对比

方式 总耗时 说明
传统串行 630ms Bugly(50) + Config(30) + Network(100) + ...
任务启动器 80ms 关键任务(80ms) + 后台并行

优化幅度:约 87%


六、监控与验证

6.1 线下监控工具

命令行快速测量

bash 复制代码
# 测量单次启动
adb shell am start -W -S com.yourapp/.MainActivity

# 自动化测量 10 次
for i in {1..10}; do
    adb shell am force-stop com.yourapp
    sleep 2
    adb shell am start -W -S com.yourapp/.MainActivity | grep TotalTime >> launch_times.txt
    sleep 3
done

# 计算平均值
awk '{sum+=$2} END {print "平均: " sum/NR "ms"}' launch_times.txt

Systrace / Perfetto

bash 复制代码
# Systrace
python systrace.py -a com.yourapp -t 10 -o launch_trace.html view am wm sched freq idle dalvik disk input

# Perfetto(推荐)
adb shell perfetto --out /data/local/tmp/perfetto_trace -c config.txt
adb pull /data/local/tmp/perfetto_trace

Android Studio Profiler

  • CPU Profiler:查看主线程执行情况
  • Memory Profiler:查看内存分配
  • Systrace:集成在 Profiler 中

6.2 线上监控方案

数据上报结构

kotlin 复制代码
data class LaunchEvent(
    val launchType: String,
    val appVersion: String,
    val osVersion: String,
    val deviceBrand: String,
    val deviceModel: String,
    val totalDuration: Long,
    val attachDuration: Long,
    val appCreateDuration: Long,
    val activityCreateDuration: Long,
    val firstFrameDuration: Long,
    val coreDataDuration: Long,
    val isUpgrade: Boolean,
    val freeMemory: Long,
    val batteryLevel: Int,
    val timestamp: Long
)

抽样上报

kotlin 复制代码
object PerformanceReporter {
    private const val SAMPLING_RATE = 0.1
    
    fun reportLaunch(event: LaunchEvent) {
        if (Random.nextDouble() > SAMPLING_RATE && !BuildConfig.DEBUG) {
            return
        }
        reportToPlatform(event)
    }
}

告警配置

告警级别 触发条件 处理方式
严重 P95 > 2000ms 通知研发群 + 自动提单
警告 P95 > 1500ms 周会重点关注

6.3 代码埋点完整方案

kotlin 复制代码
object LaunchMetrics {
    private var processStart: Long = 0
    private var attachBaseContextEnd: Long = 0
    private var applicationCreateEnd: Long = 0
    private var firstFrameTime: Long = 0
    
    fun onAttachBaseContextStart() {
        processStart = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Process.getStartUptimeMillis()
        } else {
            SystemClock.uptimeMillis()
        }
        Trace.beginSection("Launch_AttachBaseContext")
    }
    
    fun onAttachBaseContextEnd() {
        attachBaseContextEnd = SystemClock.uptimeMillis()
        Trace.endSection()
    }
    
    fun onApplicationCreateEnd() {
        applicationCreateEnd = SystemClock.uptimeMillis()
        Trace.endSection()
    }
    
    fun registerFirstFrameListener(view: View) {
        view.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                firstFrameTime = SystemClock.uptimeMillis()
                view.viewTreeObserver.removeOnPreDrawListener(this)
                reportFullLaunchMetrics()
                return true
            }
        })
    }
    
    private fun reportFullLaunchMetrics() {
        val total = firstFrameTime - processStart
        Log.d("LaunchMetrics", "总耗时: ${total}ms")
    }
}

七、总结与最佳实践

7.1 优化 Checklist

  • Application 中区分进程初始化
  • 必要/非必要任务分离
  • 使用任务启动器并行执行异步任务
  • 布局层级优化(< 10 层)
  • 使用 ViewStub 延迟加载
  • 使用 AsyncLayoutInflater 异步加载
  • SplashScreen API 消除黑白屏
  • MultiDex 主 dex 精准配置
  • 线下 Systrace 监控
  • 线上性能监控 + 告警

7.2 优化优先级

  1. P0 - 核心优化:Application 初始化优化、任务启动器
  2. P1 - 重要优化:布局优化、首帧加速
  3. P2 - 一般优化:MultiDex 优化、细节调优

7.3 持续优化流程

markdown 复制代码
1. 建立基准线
    ↓
2. 实施优化
    ↓
3. 验证效果(线下 + 线上)
    ↓
4. 监控告警
    ↓
5. 发现劣化
    ↓
6. 回归步骤 2

7.4 常见误区

误区 正确做法
所有任务都放后台 必须在主线程的任务(如 Bugly)必须放主线程
过度优化主 dex 平衡启动速度和运行时性能
只看 P50 不看 P95 P95/P99 更影响用户体验
只优化不监控 建立完整的监控告警体系

附录

A. 完整目录结构

css 复制代码
app/src/main/java/com/yourapp/
├── MyApplication.kt
├── LaunchMonitor.kt
├── init/
│   ├── InitBuglyTask.kt
│   ├── InitConfigTask.kt
│   ├── InitNetworkTask.kt
│   ├── InitDatabaseTask.kt
│   └── ...
└── launcher/
    ├── Task.kt
    ├── TaskDispatcher.kt
    ├── TaskManager.kt
    └── TaskLauncher.kt

B. 相关文档

相关推荐
图导物联2 小时前
医院导航定位系统落地部署、性能优化与问题解决方案
性能优化·医院导航定位
huangdong_2 小时前
电商图片下载工具性能优化:从页面加载到文件保存的完整链路
性能优化
_李小白2 小时前
【android opencv学习笔记】Day 23: 分水岭图像分割
android·opencv·学习
ch_ziyuan2 小时前
跨平台APP封装分发系统搭建:iOS免签+安卓防报毒+IPA签名一体化
android·ios
愈努力俞幸运2 小时前
python 三引号
android·开发语言·python
恋猫de小郭2 小时前
AI 时代,谷歌都在 Android 官方做了哪些支持?
android·前端·flutter
游戏开发爱好者82 小时前
React Grab工具详解:AI助力Vue3、Svelte和Solid前端元素调试
android·ios·小程序·https·uni-app·iphone·webview
黄林晴2 小时前
Android 性能新利器!APA 公开测试版上线
android·性能优化
tongyiixiaohuang2 小时前
MySQL与钉钉数据同步的灵活高效方案详解
android·mysql·钉钉