第28章:Logcat 与 Trace:结合 Systrace 进行深度性能分析

性能分析,说白了就是找卡顿、追内存、揪CPU。

Logcat 能告诉你「发生了什么」,但很难告诉你「为什么这么慢」。这时候,Systrace 就派上用场了。我个人习惯把 Logcat 和 Systrace 当成一对搭档——一个负责记录事件,一个负责绘制时间线。

28.1 为什么需要 Systrace?

Logcat 的输出是线性的文本流。你看到一行日志说「onClick 耗时 500ms」,但你不知道这 500ms 到底花在了哪里。是主线程被阻塞了?还是 Binder 调用卡住了?

Systrace 能回答这个问题。它从内核层、系统服务层、应用层同时抓取数据,生成一张时间轴图。你可以看到每个线程在做什么,什么时候被调度,什么时候在等锁。

核心区别:

  • Logcat:记录事件日志,适合排查逻辑错误
  • Systrace:记录时间线,适合排查性能瓶颈

我在项目中遇到过一个问题:某个页面滑动掉帧,Logcat 里没有任何异常日志。但 Systrace 一开,立马发现是 GPU 渲染超时,主线程在等 vsync 信号。嗯,这种问题光靠 Logcat 根本看不出来。

28.2 如何同时抓取 Logcat 和 Systrace?

你可以在抓取 Systrace 的同时,把 Logcat 也带上。这样,时间线上就能看到对应的日志事件。

# 抓取 10 秒的 Systrace,同时包含 Logcat 标签 "MyApp"
python systrace.py -t 10 -a com.example.myapp -o trace.html \
  --logcat="*:V"

这个命令会生成一个 HTML 文件。打开后,你会在时间轴下方看到 Logcat 面板。点击某个日志条目,时间轴会自动定位到对应的时间点。

我的习惯:在关键代码路径上加自定义 Trace 标签,然后在 Logcat 里打印同样的 tag。这样在 Systrace 里一眼就能找到对应的区间。

28.3 在代码中埋 Trace 点

光靠系统自带的 Trace 点不够细。你需要在关键函数里手动加 Trace 标记。

import android.os.Trace;

public void loadData() {
    Trace.beginSection("loadData");
    try {
        // 你的业务逻辑
        fetchFromNetwork();
        parseJson();
        updateUI();
    } finally {
        Trace.endSection();
    }
}

抓取 Systrace 时,加上 -a com.example.myapp 参数,就能看到这些自定义标签。

我曾经在某个项目里,发现一个列表加载慢。加了 Trace 后才发现,问题不在网络请求,而在 parseJson() 里有个 O(n²) 的循环。你看,没有 Trace 点,你根本不知道瓶颈在哪。

28.4 结合 Logcat 定位具体原因

Systrace 告诉你「哪里慢」,Logcat 告诉你「为什么慢」。两者结合,效果翻倍。

举个例子:

  1. Systrace 显示主线程在 onBindViewHolder 里卡了 200ms
  2. Logcat 里看到 W/ImageLoader: decode耗时 180ms
  3. 结论:图片解码太慢,需要优化缓存策略

注意:Systrace 抓取期间,Logcat 的日志量可能会很大。建议只过滤你关心的 tag,否则生成的 HTML 文件会非常庞大,打开都费劲。

28.5 实战:分析启动速度

应用启动慢,是性能优化的重灾区。我一般这么操作:

  1. 在 Application.onCreate() 和 MainActivity.onCreate() 里加 Trace 标签
  2. 在关键初始化方法里加 Logcat 日志,记录耗时
  3. 同时抓取 Systrace 和 Logcat
  4. 分析时间轴,找到最耗时的阶段
// Application 中
@Override
public void onCreate() {
    Trace.beginSection("Application.onCreate");
    super.onCreate();
    initSDK();
    initDB();
    Trace.endSection();
}

// 日志输出
Log.d("AppStart", "initSDK 耗时: " + (end - start) + "ms");

打开 Systrace 后,你会看到类似这样的时间线:

Systrace 时间线示例:应用启动 0ms 500ms 1000ms 主线程 Application.onCreate MainActivity.onCreate onResume 网络线程 HTTP请求 解析 渲染线程 第一帧 掉帧 Logcat D/AppStart: initSDK 耗时 180ms D/AppStart: initDB 耗时 220ms 应用代码 Activity生命周期 UI绘制

从这张图里,你能直观地看到:

  • Application.onCreate 占了 200ms,其中 initDB 是瓶颈
  • 网络请求和 UI 初始化是并行的,但渲染线程在第一帧之后出现了掉帧
  • Logcat 日志和 Trace 时间线完美对应

28.6 常见坑与避坑指南

我踩过不少坑,分享几个给你:

  • Trace 标签嵌套错误:beginSection 和 endSection 必须成对出现,且不能交叉。我曾经在 try-finally 里漏了 endSection,导致后面所有 Trace 都乱了。
  • Logcat 过滤太宽:抓取时如果不过滤,日志量可能上百万条。Systrace 打开后直接卡死。建议只抓 *:I 或指定 tag。
  • 时间不同步:如果 Logcat 和 Systrace 的时间戳不是同一个时钟源,对不上。好在 Android 系统已经统一了,但如果你用 adb logcat -v time 抓的日志,时间格式是 human readable,没法直接对齐。建议用 -v epoch 输出时间戳。

我的建议:在开发阶段就养成加 Trace 标签的习惯。不要等到性能出问题了再补。就像写日志一样,Trace 标签也是代码的一部分。

28.7 总结

Logcat 和 Systrace 不是二选一的关系。它们是互补的。Logcat 告诉你「发生了什么」,Systrace 告诉你「什么时候发生的、花了多久」。两者结合,你才能完整地理解一个性能问题。

嗯,说白了就是:Logcat 是文字版,Systrace 是图形版。你写代码时多埋几个 Trace 点,抓数据时带上 Logcat,分析时两边对照着看。这套组合拳打下来,大部分性能问题都藏不住。


公众号:蓝海资料掘金营,微信deep3321