剖析而不是猜测
启动慢、滑动卡,最怕的是凭感觉优化一通、结果纹丝不动。Android Studio 的 Profiler 把每一段耗时画成时间线,瓶颈一目了然。性能剖析的纪律是:先量、再改、再量。
没有火焰图的优化都是玄学:先看哪个函数真的吃时间,再决定动哪。
这篇从启动链路讲到卡顿、内存,把 Android 性能剖析走一遍。
启动链路时间线
冷启动依次经过:进程创建、Application onCreate、Activity onCreate、首帧绘制。每一段都可能被同步初始化拖长。看时间线先找最长的那段,通常就在 Application 的初始化里。
// 在启动关键路径埋点
val t0 = System.nanoTime()
initSdk()
Log.d("boot", "initSdk cost ${(System.nanoTime()-t0)/1e6}ms")
用埋点把每段耗时量化,配合 Profiler 时间线定位。
用 System Trace 看启动
System Trace 录制一次冷启动,能看到主线程每一刻在干什么、卡在哪一帧。主线程被某段同步代码占住的那段红色长条,就是要优化的目标。
剖析工具速查
- System Trace启动与帧时间线。
- CPU Profiler方法级火焰图。
- Memory Profiler堆与泄漏。
- Energy Profiler能耗与唤醒。
火焰图找 CPU 瓶颈
CPU Profiler 录一段,看火焰图:越宽的柱子占的 CPU 越多。找到业务函数里最宽的那根,就是要优化的热点。常见是主线程做了 IO、序列化或大对象构建。
卡顿与掉帧定位
滑动卡是因为某帧超过 16ms 没画完。Systrace 里那一帧对应的红色长条,就是阻塞主线程的代码。把它移出主线程,或拆成异步,帧率就回来了。
内存剖析
Memory Profiler 看堆曲线与对象分配。怀疑泄漏时,触发一次 GC 再 dump 堆,看哪个类的实例数异常多。常见是单例持有 Context、静态集合没清理。
基线与回归
把冷启动、帧率、内存峰值记成基线,每次发版跑一遍。性能是会退化的,新功能可能悄悄把启动拖慢。没有回归监控,优化成果就守不住。
- 每次优化前后都量一遍。
- 把基线固化进 CI。
- 性能退化要当 bug 处理。
实战:定位一次启动卡顿
录一次冷启动 System Trace,看到 Application.onCreate 里一段初始化占了 400ms。
// 移出主线程,延迟到首页展示后
object : Thread() {
override fun run() { initHeavySdk() }
}.start()
把重初始化挪到子线程,冷启动立刻少了一大截。性能剖析的闭环就是这样:量出来、找到根因、改掉、再量确认。