目录
一、启动类型概述
1.1 三种启动类型
| 类型 |
定义 |
特点 |
| 冷启动 |
进程从 0 开始创建 |
耗时最长,重点优化 |
| 温启动 |
进程已存在,Activity 重建 |
耗时中等 |
| 热启动 |
Activity 从后台切前台 |
耗时最短 |
1.2 冷启动完整流程
用户点击 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
<!-- AndroidManifest.xml -->
<application
android:vmSafeMode="true">
</application>
3.2 Application 初始化阶段
优化策略
- 区分进程:只在主进程初始化
- 必要/非必要分离:必要任务同步,非必要异步
- 延迟初始化:使用 IdleHandler 在空闲时执行
- 任务启动器:用专门的任务启动器管理
代码示例:进程区分
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 延迟初始化
Handler(Looper.getMainLooper()).postIdle {
initPush()
initMap()
initAnalytics()
false // 只执行一次
}
3.3 Activity 创建阶段
优化策略
- 布局优化:减少层级、使用 merge/include
- 异步加载:AsyncLayoutInflater 异步 inflate
- ViewStub:延迟加载非必须 View
- 数据预加载:提前加载首页数据
代码示例:布局优化
<!-- 使用 merge 减少层级 -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/header" />
<include layout="@layout/content" />
</merge>
<!-- 使用 ViewStub 延迟加载 -->
<ViewStub
android:id="@+id/stub_detail"
android:layout="@layout/layout_detail"
android:inflatedId="@+id/fl_detail" />
代码示例:异步加载布局
AsyncLayoutInflater(this).inflate(
R.layout.activity_main,
null
) { view, _, _ ->
setContentView(view)
initViews(view)
}
3.4 首帧绘制阶段
优化策略
- SplashScreen API:使用官方启动页
- 消除黑白屏:设置 windowBackground
- 过度绘制:减少不必要的背景
- 简化首屏:首屏只显示必要内容
代码示例:SplashScreen API
<!-- 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>
// 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+
// app/build.gradle.kts
android {
defaultConfig {
multiDexEnabled = true
}
buildTypes {
release {
multiDexKeepProguard = file("multidex-keep.pro")
}
}
}
AGP 7.x 及以下
// app/build.gradle
android {
defaultConfig {
multiDexEnabled true
}
buildTypes {
release {
multiDexKeepProguard file('multidex-keep.pro')
}
}
}
# 核心启动类
-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
- Android Studio → Build → Analyze APK
- 选择 APK 文件
- 查看 classes.dex 的内容
方法 2:命令行
# 使用 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 架构设计
TaskLauncher (入口)
↓
TaskManager (任务管理)
↓
TaskDispatcher (任务分发)
├─ 主线程 Executor
├─ IO 线程池
├─ CPU 密集线程池
└─ 单线程 Executor
5.2 核心代码
Task.kt - 任务定义
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 - 任务分发器
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 - 任务管理器
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 - 启动器入口
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 实际使用示例
定义初始化任务
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 中集成
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()
}
}
监控器实现
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 线下监控工具
命令行快速测量
# 测量单次启动
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
# 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 线上监控方案
数据上报结构
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
)
抽样上报
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 代码埋点完整方案
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
7.2 优化优先级
- P0 - 核心优化:Application 初始化优化、任务启动器
- P1 - 重要优化:布局优化、首帧加速
- P2 - 一般优化:MultiDex 优化、细节调优
7.3 持续优化流程
1. 建立基准线
↓
2. 实施优化
↓
3. 验证效果(线下 + 线上)
↓
4. 监控告警
↓
5. 发现劣化
↓
6. 回归步骤 2
7.4 常见误区
| 误区 |
正确做法 |
| 所有任务都放后台 |
必须在主线程的任务(如 Bugly)必须放主线程 |
| 过度优化主 dex |
平衡启动速度和运行时性能 |
| 只看 P50 不看 P95 |
P95/P99 更影响用户体验 |
| 只优化不监控 |
建立完整的监控告警体系 |
附录
A. 完整目录结构
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. 相关文档