android启动流程与速度优化

📅 2026/8/24 13:18:18
android启动流程与速度优化
要想优化进程的启动需要懂得进程启动流程会调用对应的什么方法以及通过profile查看对应的方法消耗了多少时间而一个进程启动包括了系统进程之间的调用方法还有用户自己本身进程内部的调用方法所以就要懂得看整个链路对应的方法都消耗了多少时间用户自己本身进程内部调用方法比如常见的在主线程io 操作网络操作或者布局嵌套太深等都会影响启动时间以及要如何根据当前业务去优化排这些任务的顺序和依赖问题进程启动流程简略如下a.用户点击桌面appLauncher 进行通过 Binder 和system_server 通信 要 开起一个进程b.system_server 通过 Socker通信 与 Zygote 进程 要fork 一个新的进程 Zygote做了fork 一个新的进程c.新创建出来的进程 通过 Binder 通信 告诉 system_server进程要 和你绑定在一起毕竟 system_server进程是 统一调度管理d.system_server进程绑定成功后 通过 Binder 通信 新进程绑定成功了你可以起开启了e.新进程 Binder线程 接收到 system_server进程 消息后 通过Handle 通信 发消息 H.LAUNCH_ACTIVITY 消息给 主线程 的消息队列中 (因为Binder线程不做ui操作f. 页面第一次显示有画面是system_server的wms 创建Starting Window而这个Starting Window显示的画面是从你第一个activity设置的theme 的属性windowSplashScreenBackground(不同版本不同属性如上图配置的!--LaunchTheme第一个启动actvity 设置的主题StartingWindow显示的内容--style nameTheme.MyApplication.LaunchparentTheme.SplashScreen!--API31系统SplashScreen背景色中间图标--item namewindowSplashScreenBackgroundcolor/splash_background/itemitem namewindowSplashScreenAnimatedIconmipmap/ic_launcher/itemitem namewindowSplashScreenIconBackgroundColorcolor/purple_500/item!--StartingWindow设置完这个主题后下一个主题是style/Theme.MyApplication--item namepostSplashScreenThemestyle/Theme.MyApplication/item!--API24~30老设备StartingWindow整屏背景--item nameandroid:windowBackgrounddrawable/launch_background/item/style如果你不设置这些属性那么默认是默认白/黑Material 浅色主题多为白),而这个白/黑/启动屏画面会持续显示到你第一个activity setContentView设置的画面流程走完Starting Window被移除显示真正第一个页面内容为止如上图是显示到大概 onResume 执行完 measure / layout / draw绘制结束onWindowFocusChanged获取到焦点能被用户触发点击。那么如果在Starting Window显示再到第一个activity 显示的view显示中间对应的链路和方法耗时太长那么就会导致一直卡在启动页。如上图当第一个activity界面被显示出来 LogcatActivityTaskManager: Displayed … XXXms 大体上可以理解为代表进程的启动时间或者通过 adb 开启一个进程启动activity 也可以看到 对应的时间adb shell am start -S -W 包名 启动activity名字从上面应用启动链路中我们知道一个app从启动到界面完全显示可交互有不同阶段一个是framework阶段一个是应用层阶段这里只讲应用阶段在应用层阶段我们有Application的onCreate阶段我们通常在这个阶段去做整个应用的sdk和网络初始化数据库或者对第一个activty或者fragment要显示的内容进行初始化当然不应该把任务全都堆在这里那么要如何 处理这部分的内容呢是直接开一个线程异步去对所有的初始化这会造成时间和cpu资源的浪费其实对应业务每个任务与每个任务之间都有对应的依赖性也就是一个任务需要等另外一个任务执行完任务执行之间是可以并行还是并发进而来启动优化也就是对所有任务之间做一个管理顺序任务之间的依赖是同步异步本质上其实就是数据结构算法问题核心思路上可以利用DAG 拓扑排序 管理启动任务任务与任务之间 同步/异步协调可用CountDownLatch CompletableFuture / 协程 / App Startup框架 更好维护。阶段来到 第一个activity的oncreate 到onResume这个链路这个过程尽量保持布局不要多度绘制为了快速加载MainActivity所需要数据可以采用数据从缓存获取的方式如果数据异步获取到结果后就更新缓存所要数据接口最好只有一个不要弄太多了接口需要显示多少 获取多少采用懒加载的模式还有一个办法看场景所需只适合「可以晚点做」的轻量任务Looper.myQueue().addIdleHandler{false// // false执行一次后移除true下次空闲的时候 还会再执行//一般都是falsetrue慎用}IdleHandler 主线程 MessageQueue 空档回调 我们知道主线程有很多消息message有个消息队列而当线程空闲没事做等待的时候就会去调用这个idle但是他还是在主线程所以你不要指望在这里去做很耗时的任务任务太重照样卡 UI可以把 非紧急初始化 丢进 IdleHandler会 排到首帧之后、队列空档 再跑不挤占启动关键路径。override funonCreate(...){super.onCreate(savedInstanceState)setContentView(...)Looper.myQueue().addIdleHandler{// 首帧画完后、主线程空档再跑initThirdPartySdk()preloadData()false// 只执行一次}}总结就是别「一条线程异步全包」也别「Application 里同步全做」—— 按依赖和优先级管任务 才是正路。首页activity画面必须尽快展示别堵在中间的链路。a. 列出所有启动任务SDK、DB、配置、账号…b. 画依赖图谁依赖谁c. 拓扑排序 → 执行顺序d. 同一层无依赖 → 线程池并行e. 有依赖 → 前置完成再启动后置DAG 管不能乱并行f. 非首屏必须 → 延后到首帧后 / IdleHandlerg. 需要「等多路完成合适调度」→ CountDownLatch / CompletableFuture / 协程通过cpu profile 工具 录制 查看每个方法跑了多少时间如图总有3个工具下面的弹窗代表录制时机点now 代表进程app已经开启运行中了你要现在就开始录制Process start 代表进程重新从0 开启冷启动运行并且进行录制System Trace 我理解为他是看系统进程级别别对应的方法的CPU 各核调度、线程状态Running / Sleeping / Blocked主线程、RenderThread、Binder 线程活动VSYNC、Choreographer、doFrame、布局、绘制系统事件 你代码里的 Trace.beginSection(“xxx”) 自定义片段适合场景启动慢看 bindApplication、handleLaunchActivity、首帧滑动卡顿看 Choreographer#doFrame 是否超 16msCallstack Sample 定时采样录制例如每 ms 级拍一次 当前调用栈快照Java/Kotlin Method Recording 我理解是能够很细记录进程app内我们自己写的代码层级对应方法总结下3个工具就是System Trace → 看「整条流水线」App 系统 渲染Callstack Sample → 看「谁最常出现在 CPU 上」抽查一般不常用Method Recording → 看「每个 Java 方法精确待了多久」开发阶段采用StrictMode 模式StrictMode 是 Android 自带的开发期检测工具用来发现「不该在主线程 / 进程里做的事」并在 Logcat 里报警或让 App 崩溃// Application.onCreate中调用if(BuildConfig.DEBUG){// 主线程违规检测当磁盘读写网络操作StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().detectDiskReads()// 磁盘读.detectDiskWrites()//磁盘写.detectNetwork()//网络操作.penaltyLog()// 打 Logcat 警告// .penaltyDialog() // 弹窗可选// .penaltyDeath() // 直接崩溃开发期可开很激进.build())// 对整个进程级泄漏等进行检测StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects()//SQLite 用完没关.detectLeakedClosableObjects()//InputStream、Cursor 等 Closeable 没关.detectActivityLeaks()//Activity 销毁了还被静态变量、单例等持有.detectLeakedRegistrationObjects()//广播、Service 等注册后没反注册.penaltyLog().penaltyDeath()// 泄漏建议开发期直接 crash好定位.build())}学习了上面的知识后启动慢推荐流程adb am start -W → 总耗时多少System Trace / Method Recording → 慢在哪个方法 根据上面的启动流程查找可疑的方法改代码 → 再测 TotalTime 对比