Android优化工具Systrace

Systrace

简单的性能优化,可能很多人都会。比如以下几个优化 UI 渲染的方法,想必很多人都知道

  1. 使用“设置 --> 开发者选项 --> 调试 GPU 过度绘制”,根据屏幕显示的不同颜色来区分是存在过度绘制,从而排查该界面的 xml 文件,去除不必要的 background,消除过度绘制

  2. 通过 Layout Inspector 查看布局层级,排查是否存在多层无用的嵌套(由于 Hierarchy Viewer 已经被废弃,如果使用 3.1 及更新版本的 Android Studio,使用 Layout Inspector 查看布局会更加方便)

  3. xml 中使用 ViewStub & merge 标签,优化布局层级

  4. ......

上面的这些点当然很重要,但是在某种程度下,上面的这些做法已经力不从心了,我们需要通过其他方式来达到优化性能的目的

俗话说的好,工欲善其事,必先利其器,使用一个好的工具当然可以让我们事半功倍,由于 TraceView 过于严重的运行时开销,使得 TraceView 测量的很多数据偏差较大,所以 Google 现在强推 systrace,systrace 是一个非常强大的性能分析工具。

systrace 可以从系统层面上,收集并分析设备运行时的所有进程的时间信息,它从 Android 内核中,比如:CPU 调度、磁盘活动和 app 线程中收集信息,然后生成如下图所示的 html 文件,需要说明的是:生成的 trace.html 文件必须用 Chrome 浏览器打开才可以正常的浏览使用.

image

图片来源:systrace。如上图所示,‘Frames’ 那一行里面的每一个小圆圈就代表着每一帧,用不同的颜色来代表是否正常的渲染,如果某一个小圆圈用黄色/红色表示,则表明这一帧的渲染可能存在问题

好,接下来我们来看下如何使用 systrace 工具

二. 如何使用

我使用的 Mac 电脑,所以以下操作都是在 Mac 上进行的,在 Windows 系统上应该也大同小异。

2.1 准备工作

在使用 systrace 之前,需要做以下几个准备工作

  1. 较新的 Android SDK Tools

  2. 需要 PC 端配合,PC 端安装了 Python 且配置在了系统环境变量中

  3. 调试的设备需要是 4.3(API Level 18)以上的,系统越高,可以收集到的信息越多,越有利于分析,分析的应用需要是 debug 包

  4. 通过 usb 将 Android 设备和 PC 连接成功,处于可调试的状态

至此,准备工作已完成

2.2 使用

2.2.1 使用方法

通过 Terminal 进入到 /Android/sdk/platform-tools/systrace/ 目录下:

cd /Users/(个人电脑用户名)/Library/Android/sdk/platform-tools/systrace

或者直接,不知道systrace的目录的情况下用以下方法,然后使用命令pwd,即可知道当前所在的完整路径:

cd $ANDROID_HOME/platform-tools/systrace
image.png

这个时候,在设备上操作应用,使应用进入到待调试的状态,比如需要调试某个页面 RecyclerView ,则进入该页面

然后在 Terminal 里面运行如下命令,其中 [options] [categories] 都是需要输入的参数:

./systrace.py [options] [categories]

举例:
调用systrace来记录10秒钟内的设备进程,包括图形进程,并生成mynewtrace.html报告:

python systrace.py --time=10 -o /Users/didi/Downloads/mynewtrace.html gfx

参数信息说明

那么 [options] 和 [categories] 都包括哪些参数呢?

options参数表
options description
-o < FILE > 指定输出的文件,如:-o mynewtrace.html。如果没有指定此参数,systrace会将您的报告保存到systrace.py所在的同一目录中,并将其命名为trace.html
-t N, –time=N 指定 systrace 的持续时间,如 -t 10,表示记录 10s 钟,<T>的单位是 s 秒。如果没有指定此参数,在按下回车键 Enter 健时结束 systrace
-b N, –buf-size=N buffer大小(单位kB),用于限制trace总大小,默认无上限
-a < APP_NAME >,–app=< APP_NAME > 指定特定的应用,比如:-a com.lijiankun24.shadowlayout。如果在此应用中使用了 Trace.beginSection("tag") 和 Trace.endSection,默认情况下,这些标签是不会生效的,除非你通过此命令指定该应用,在 systrace 输出的 html 文件中才会记录该标签标记的方法的信息
-h , --help 显示帮助信息
-l,--list-categories 列出可用于连接设备的跟踪categories类别
-k functions,--ktrace=functions 跟踪中指定的特定内核函数的活动,以逗号分隔的列表
-e device-serial,--serial=device-serial 跟踪指定的设备序列号标识的特定连接设备
catagories参数表
category description
sched CPU 的调度信息,可以看到 CPU 的每个核在具体的时间点执行了什么线程
gfx Graphics 渲染系统,包括 SurfaceFlinger、VSync、Texture、RenderThread 的信息
input 输入事件系统,记录键盘输入、触摸等事件信息
view View 视图系统,常见的 View 的 onMeasure、onLayout、onDraw 都记录在此系统中
webview WebView
wm WindowManager 的调用信息记录在此模块中
am ActivityManager 的调用信息记录在此模块中
sm Sync Manager
audio Audio
video Video
camera Camera
hal Hardware Modules
app Application
res Resource Loading
dalvik 虚拟机相关信息,比如 GC 垃圾回收信息
rs RenderScript
bionic Bionic C Library
power Power Management
irq IRQ Events
freq CPU Frequency
idle CPU Idle
disk Disk I/O
mmc eMMC commands
load CPU Load
sync Synchronization
workq Kernel Workqueues
memreclaim Kernel Memory Reclaim
regulators Voltage and Current Regulators

生成 trace.html 文件大概就是这样,并不复杂,下面介绍几个查看此 html 文件的快捷键,通过下面几个常用的快捷键,可以方便的查看 html 文件

快捷键

查看Systrace生成的trace.html,浏览器打开界面如下:

Image3.png

(图源来自官网)
颜色块
每块颜色占据的长度即为该系统或者自定义trace等执行所占据的时间长度

Alerts
含有三角状的圆圈图标,对应出现警告的位置,点击可以在右边栏Alerts查看具体警告内容;
警告会告诉你可能丢帧或者卡顿等的原因

Frame
含有F字母的圆圈图标,对应每一帧开始的位置,不同颜色有不同意义;
绿色表示正常,当颜色为橙色或者红色时,意味着这一帧超过16.6ms(即发现丢帧);

Kernel
(上图为四核CPU)显示每个CPU各自执行的系统方法或自定义trace块,以及占据的时间长度

SurfaceFlinger
surfaceFilnger,进程id为118,显示系统方法以及占据的时间长度

com.android.janktown
应用进程,进程id为13409,显示应用进程内各个线程等信息
每个线程有颜色表示各自不同的状态

  • 灰色:正在休眠。
  • 蓝色:可运行(它可以运行,但是调度程序尚未选择让它运行)。
  • 绿色:正在运行(调度程序认为它正在运行)。
  • 红色:不可中断休眠(通常在内核中处于休眠锁定状态)。可以指示 I/O 负载,在调试性能问题时非常有用。
  • 橙色:由于 I/O 负载而不可中断休眠。
Image7.png

分析trace.html图形信息之前,先了解下快捷键
点击浏览器界面上左上角“?”,可以查看到各个快捷键提示

快捷键 作用
w 放大时间轴,[+shift]速度更快
s 缩小时间轴,[+shift]速度更快
a 左移时间轴,[+shift]速度更快
d 右移时间轴,[+shift]速度更快
f 放大当前选定区域
m 标记当前选定区域
Right Arrow 选中所选时间轴上的下一个事件
Left Arrow 选中所选时间轴上的上一个事件
v 高亮VSync
g 切换是否显示60hz的网格线
0 恢复trace到初始态,这里是数字0而非字母o
h 切换是否显示详情
/ 搜索关键字
enter 显示搜索结果,可通过← →定位搜索结果
` 显示/隐藏脚本控制台
? 显示帮助功能

分析trace.html

Systrace可以直观的看到掉帧引起的界面卡顿

如下图所示,是一个放大后的 trace.html 的局部图。我们都知道 Android 系统中的 60 fps 概念,也就是 1s 内会渲染 60 帧,渲染一帧需要 16.6 ms,下图中用红色框起来的就是每一个 frame,如果在 16.6 ms 内完成了渲染,则该帧是绿色的,如果渲染超过了 16.6 ms,则呈现出黄色或者红色

frame-unselected.png
image

图片来源 systrace

在上图中,选中存在问题的黄色帧以后,需要注意两部分,如下所示

  1. 第一个红色框中,高亮的部分是这一帧在 UI 线程和 RenderThread 线程中都调用了哪些方法

  2. 第二个红色框中,展示了一些信息,包括非常有用的该帧出问题的原因(Alert & Description),这些都是系统给出的存在的问题和优化建议

image

点击F,使用快捷键f放大该帧,可以选择m高亮该选区,查看该帧的所有系统trace块执行时间

Image8.png

查看下面面板的Frame里的信息

ListView recycling takiing too mush time per frame.Ensure your Adapter#getView() binds data efficiently

Alerts选项卡可以查看每个警报以及设备触发每个警报的次数。如果我们在上图中,选中右上角的 Alerts tab,会出现如下图所示的信息,它告诉我们在这段时间内该问题出现的频次,比如下图所示的:Inefficient ListView recycling/rebinding 共出现了 55 次。

主要问题是在ListView回收和重新绑定中花费了太多时间。

如果你在UI线程上看到了太多的工作,你需要找出哪些方法消耗了太多的CPU时间。

可以把 Alerts tab 当做一个需要处理的 bug 列表,这个列表中的问题都不同程度上的对我们的帧渲染造成了问题。有时候可能只是几行代码的微小改动和优化,却可以优化我们很多的问题

image

3.2 为自己的应用添加 Trace 信息

默认情况下,systrace 都只能记录、收集系统层面的信息,比如 WindowManagerActivityManager、以及 Dalvik 等等模块的,有没有什么办法也记录收集自己应用中的一些信息呢?

Android 是提供了这样的 Api 的,这个类是 Trace 类,使用 Trace 类记录自己应用中的信息其实并不难,如下所示,有如下几点需要注意

  1. Trace.beginSection(String sectionName)Trace.endSection() 需要成对出现,为保证每个 Trace.beginSection(String sectionName) 都会有对应的 Trace.endSection(),建议使用 try {……} finally {……}

  2. 如果在 Trace.endSection() 之前有多个 Trace.beginSection(String sectionName)Trace.endSection() 会匹配离它最近的一个未匹配过的 Trace.beginSection(String sectionName)

  3. Trace.beginSection(String sectionName)Trace.endSection() 需要在同一线程中

public class CardViewListActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Trace.beginSection("CardViewListActivity_onCreate");
        try {
            setContentView(R.layout.activity_card_view_list);
            RecyclerView recyclerView = findViewById(R.id.rv_card_view);
            recyclerView.setLayoutManager(new LinearLayoutManager(CardViewListActivity.this));
            recyclerView.setAdapter(new CardViewListAdapter());
        } finally {
            Trace.endSection();
        }
    }

    private static class CardViewListAdapter extends RecyclerView.Adapter<CardViewHolder> {
        @NonNull
        @Override
        public CardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            Trace.beginSection("CardViewListAdapter_onCreateViewHolder");
            CardViewHolder viewHolder;
            try {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_card_view_list, null);
                Trace.beginSection("CardViewListAdapter_onCreateViewHolder_newHolder");
                try {
                    viewHolder = new CardViewHolder(view);
                } finally {
                    Trace.endSection();
                }
            } finally {
                Trace.endSection();
            }
            return viewHolder;
        }
    }
}

在自己应用的代码中添加如上代码之后并没有结束,还有一点非常重要,在执行 systrace 命令的时候,需要通过 -a <package_name> 指定应用包名,这样才会记录、收集到自己应用中添加的 trace 信息,如下所示:

./systrace.py -t 10 -o mytrace.html -a com.lijiankun24.shadowlayout sched freq idle am wm gfx view binder_driver hal dalvik camera input res

在生成的 trace.html 文件中,可以通过右上角的查找,找到 sectionName,就可以查到该 Trace 的记录信息

image

3.3 原理浅析

其实 systrace 的思想很简单,就是在一些关键路径中打 log,通过 log 的开始和结束就可以得到一个方法的执行时间信息,然后将这些 log 收集起来,就可以得到关键路径的运行时间信息,进而得到整个系统的运行性能信息。

在 Android 应用、Android Framework 和 native 层通过不同的方法或类打 log

3.3.1 Android Framework

import android.os.Trace;
Trace.traceBegin(long traceTag, String methodName)
Trace.traceEnd(long traceTag)

比如在 ActivityThread 中的内部类 H.handleMessage(Message msg) 方法如下所示

image

在 Android Framework 中是通过 Trace.traceBegin(long traceTag, String methodName) 方法打 log 的,传入的 traceTag 是 Trace 类中的常量类,如下所示

image

其实这里的 Trace 常量值,和我们在执行 ./systrace [options] [categories] 时,传入的 [categories] 值对应的

3.3.2 Android 应用

对应的 traceTag 名称是 TRACE_TAG_APP,在使用 systrace.py 命令运行时,需要通过 -a <package-name> 指定应用的包名,才可以收集到埋的 tag

import android.os.Trace;
Trace.beginSection(String sectionName)
Trace.EndSection()

Trace 类的源码如下,可见
traceBegin(long traceTag, String methodName)
traceEnd(long traceTag)
beginSection(String sectionName)
endSection()
最后都调用了 native 方法
nativeTraceBegin(long tag, String name)
nativeTraceEnd(long tag)

public final class Trace {
    @FastNative
    private static native void nativeTraceBegin(long tag, String name);

    @FastNative
    private static native void nativeTraceEnd(long tag);

    private Trace() {
    }

    public static void traceBegin(long traceTag, String methodName) {
        if (isTagEnabled(traceTag)) {
            nativeTraceBegin(traceTag, methodName);
        }
    }

    public static void traceEnd(long traceTag) {
        if (isTagEnabled(traceTag)) {
            nativeTraceEnd(traceTag);
        }
    }

    public static void beginSection(String sectionName) {
        if (isTagEnabled(TRACE_TAG_APP)) {
            if (sectionName.length() > MAX_SECTION_NAME_LEN) {
                throw new IllegalArgumentException("sectionName is too long");
            }
            nativeTraceBegin(TRACE_TAG_APP, sectionName);
        }
    }

    public static void endSection() {
        if (isTagEnabled(TRACE_TAG_APP)) {
            nativeTraceEnd(TRACE_TAG_APP);
        }
    }
}

3.3.3 native 层

其实 systrace 本质上是对其他工具的封装,包括 PC 端的 atrace 和设备端的 ftraceftrace 是 Linux 内核中的主要跟踪机制。systrace 使用 atrace 开启追踪,然后读取 ftrace 的缓存,并且把它重新转换成HTML格式

#include<utils/Trace.h>ATRACE_CALL();

其他

extra:
什么是atrace?什么是ftrace?

ftrace 是一种调试工具,用于了解 Linux 内核中的情况;而 atrace (frameworks/native/cmds/atrace) 使用 ftrace 来捕获内核事件;
官网简单的介绍地址:https://source.android.google.cn/devices/tech/debug/ftrace

参考:https://zhuanlan.zhihu.com/p/27331842
转载于:https://blog.csdn.net/chuyouyinghe/article/details/115657537

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容