Android App性能监控工具

"多维度分析app的性能"

Posted by Weiwq on August 15, 2021

“工欲善其事,必先利其器“

一、LeakCanary

LeakCanary 想必大家都有了解一些,主要用于分析activity、fragment的内存泄露的问题。

在主module下的gradle导入如下依赖即可

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}

在安装测试app后,点击leak的图标进入leak应用,点击Dump Heap Now即可,内存泄露引用链如下

二、BlockCanary

blockcanary 最新的一个版本是2017年发布的,已经很久没维护了,但是其原理还是值得借鉴的。作者文章

导入依赖方式:

// 当然也可以用implementation,建议用debug方式导入
debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'

需要注意的是由于涉及到读写文件,所以还需要声明对应的权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

创建一个application,并在Androidmanifest中使用

public class BlockCanaryApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        BlockCanary.install(this, new AppBlockCanaryContext()).start();
    }
}

public class AppBlockCanaryContext extends BlockCanaryContext {
    private static final String TAG = "AppBlockCanaryContext";

    // block 时会回调
    @Override
    public void onBlock(Context context, BlockInfo blockInfo) {
        super.onBlock(context, blockInfo);
        Log.d(TAG, "onBlock: " + blockInfo.model);
    }

    //卡顿阀值 ,默认是1000 ms
    @Override
    public int provideBlockThreshold() {
        return 200;
    }
}

展示dump信息的页面基于LeakCanary界面修改,可以很清楚看到哪里卡了和卡的时长

三、Perfdog

Perfdog是由腾讯出品的移动平台性能分析工具,官网点我前往,工具首页如下,默认的功能有:FPS、CPU、memory三个维度的性能。

  • 右下角可以扩展更多功能
  • 点击右上角可以开始记录数据,再点一下可以保存到云平台。

在官网登录后就可以看到对应的详细数据

四、Profiler

Perfdog 只适合用于监控CPU,内存、FPS等情况,如果想具体排查问题,还是得用Android studio自带的Profiler

profiler支持CPU、memory、network、energy维度的分析。

1、CPU

在CPU下,点击record可以开始记录一段时间内的方法耗时情况

点击stop后,就可以看具体的执行耗时情况,比如在main线程中,clickView的方法耗时长达270ms,就可以结合代码做具体的耗时分析。

2、Memory

在Memory下,可以看到每个块所占用的内存大小,如果想具体看内存分配情况,可以点击顶部的“Allocation Tracking”

在点击stop后,就会进入如下页面

  • 区域1:可以按照不同的归类来查看内存情况
  • 区域2:每个类实例对应的内存分配情况,单位是byte。点击对应的分类,可以按照该分类的内存情况升序或者降序排列。
  • 区域3:该实例对应的成员变量和引用链,对于分析内存泄露很有帮助
  • 区域4:该类的实例列表,正常列表只有一个,如果有多个,有可能发生了内存泄露。

3、Network

可以测试网络的收发速度

五、命令

1、dumpsys meminfo

可以通过如下命令查看包为 com.example.kotlindemo的内存信息(也可以查看PID对应的信息)

C:\Users> adb shell dumpsys meminfo com.example.kotlindemo
Applications Memory Usage (in Kilobytes):
Uptime: 5765033 Realtime: 5765033
** MEMINFO in pid 19462 [com.example.kotlindemo] **
                   Pss  Private  Private  SwapPss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    28070    28012       32      148    92160    34457    57702
  Dalvik Heap        0        0        0        0     8199     4100     4099
        Stack       96       96        0        0
       Ashmem       13        0       12        0
      Gfx dev     3568     3568        0        0
    Other dev        2        0        0        0
     .so mmap     7331      388     3484        9
    .apk mmap      168        0       20        0
    .ttf mmap      143        0       60        0
    .dex mmap     4965       12     3104        0
    .oat mmap      219        0        0        0
    .art mmap     8397     7728      348       84
   Other mmap      107        4        0        0
   EGL mtrack    24660    24660        0        0
    GL mtrack     2684     2684        0        0
      Unknown    31770    31732        4       27
        TOTAL   112461    98884     7064      268   100359    38557    61801

 App Summary
                       Pss(KB)
                        ------
           Java Heap:     8076
         Native Heap:    28012
                Code:     7068
               Stack:       96
            Graphics:    30912
       Private Other:    31784
              System:     6513

               TOTAL:   112461       TOTAL SWAP PSS:      268
 Objects
               Views:       18         ViewRootImpl:        2
         AppContexts:        4           Activities:        1
              Assets:        9        AssetManagers:        0
       Local Binders:       14        Proxy Binders:       33
       Parcel memory:        9         Parcel count:       21
    Death Recipients:        2      OpenSSL Sockets:       11
            WebViews:        0
 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0

这里的单位是kb,其中

  • Pss Total:实际使用的内存,将跨进程共享页也加入进来,会比在profiler中的要大一些。(重点关注之一)
  • Private Dirty:是应用独占内存大小,包含独自分配的部分和应用进程从Zygote复制时被修改的Zygote分配的内存页。(重点关注之一)
  • Private clean:是已经映射持久文件使用的内存页,比如正在被执行的代码。
  • Dalvik Heap:Dalvik 虚拟机分配的内存。
  • Java Heap:java堆大小。
  • Objects 中显示持有对象的个数,从这里我们可以分析view、activity的个数。其中,可以通过看activity的个数判断是否发生内存泄漏。

2、systrace

2.1 基本用法

systrace需要Python环境,在使用前请先配置好adb,并连接上手机。

window环境可以按照如下配置

  • python 2.7 环境:用 py -2 或者 py -3 切Python2和Python3
  • six模块:安装命令py -2 -m pip install six
  • win32con模块:安装命令py -2 -m pip install pypiwin32

Android SDK 工具软件包中提供该命令,对应路径是 android-sdk/platform-tools/systrace/ 语法如下:

python2 systrace.py [options] [categories]

可用参数如下

命令和选项 说明
-o file 将 HTML 跟踪报告写入指定的文件。如果您未指定此选项,systrace 会将报告保存到 systrace.py
所在的目录中,并将其命名为 trace.html
-t N 跟踪设备活动 N 秒。如果您未指定此选项,systrace 会提示您在命令行中按 Enter 键结束跟踪。
-b N 使用 N KB 的跟踪缓冲区大小。使用此选项,您可以限制跟踪期间收集到的数据的总大小。
-k functions 跟踪逗号分隔列表中指定的特定内核函数的活动。
-a app-name 启用对应用的跟踪,指定为包含进程名称的逗号分隔列表。
-h 显示帮助消息。

在执行完后,会自动生成一个html文件。可以用Chrome打开,在Chrome浏览器网址栏输入

chrome://tracing/

点击load 按钮,选择我们的trace文件即可

其中

  • 区域1是总CPU的使用情况
  • 区域2是指定进程的cpu使用情况
  • 区域3是用于鼠标的控制功能,从上往下依次为点击、上下左右移动、点击上下拉缩放、框定时间区域。
  • 顶部的processes可以选择你感兴趣的进程
  • 右上角”?“可以查看操作信息
  • metrics栏可以查看各项指标。

更多见浏览systrace报告

2.2 查看GC

在启动过程,要尽量减少 GC 的次数,避免造成主线程长时间的卡顿,特别是对 Dalvik 来说,我们可以通过 systrace 单独查看整个启动过程 GC 的时间。

python2 ./systrace.py dalvik -b 90960 -a com.sample.gc

对于GC的使用含义,可以参考调查RAM使用情况

2.3 自定义Trace

Android在关键系统回调添加了Trace,但是我们无法通过systrace准确获取到项目代码的耗时情况,因此需要在关键位置打label。

Android提供了android.os.Trace#beginSection()android.os.Trace#endSection 这两个接口。比如想排查onCreate方法中,耗时的情况,可以通过如下代码实现

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 指定label为 "MainActivity_setContentView"
        Trace.beginSection("MainActivity_setContentView")
        method1()
        method2()
        Trace.endSection()
    }
    private fun method2() {
        Thread.sleep(300)
    }

    private fun method1() {
        Thread.sleep(200)
    }
}

重点:要想使用自定义Label,就必须在gradle中,打开debuggable

  buildTypes {
        debug {
            debuggable true
        }
    }

执行systrace,其中-a 表示开启指定包自定义Trace label的功能

$  python2 .\systrace.py -a com.example.asm

打开应用,进入指定页面后,退出systrace。用chrome打开trace.html 文件。如下所示,可以看到systrace对性能的影响很小。

但是由于开启了debuggable,与正式的release版本在性能上会有较大差异,如果我们想尽可能的还原真实情况,那必须在非debuggable模式下执行。我们可以通过如下方式绕过debuggable的限制


public class BaseApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        try {
            Class<?> trace = Class.forName("android.os.Trace");
            Method setAppTracingAllowed = trace.getDeclaredMethod("setAppTracingAllowed", boolean.class);
            setAppTracingAllowed.invoke(null, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

如果自定义label,再配上插桩,那就能最接近真实情况下,详细分析每个方法的耗时情况。

插桩实现和库可以参考TraceFix

参考手把手教你使用Systrace

3、Perfetto

Perfetto 是 Android 10 中引入的全新平台级跟踪工具,你可以在perfetto界面中打开这些跟踪

或者可以通过命令方式打开

cd /path-to-traces-on-my-dev-machine
systrace --from-file trace-file-name{.ctrace | .perfetto-trace}

更多见系统跟踪

4、Debug接口

4.1 获取Trace文件

Android为我们提供了Debug工具,可以获取指定路径的trace文件,我们只需要在特定的位置加入如下代码,即可获取对应的trace文件

// 设置开始记录方法调用情况
Debug.startMethodTracing("/sdcard/debug.trace");

// 结束记录方法调用情况
Debug.stopMethodTracing();

将trace文件pull出来后,直接把文件拖拽到Android studio中即可。区域1为各个线程的耗时情况,区域2 为对应的火焰图。

4.2 获取Hprof文件

通过如下获取hprof文件,需要注意如下代码十分的耗性能

Debug.dumpHprofData("/sdcard/dump.hprof")

pull出来用Android studio打开如下

5、查看RAM

$ cat /proc/meminfo | head -n 4
MemTotal:        5861796 kB // 总共内存
MemFree:           90268 kB // 空闲内存
MemAvailable:    3392216 kB // 可用内存(即剩余运行内存)
Buffers:         1049552 kB // 缓存

6、查看CPU——top

$ top
Tasks: 743 total,   2 running, 737 sleeping,   0 stopped,   0 zombie
Mem:   5861796k total,  5683832k used,   177964k free,  1057640k buffers
Swap:  2621436k total,   478088k used,  2143348k free,  1994168k cached
800%cpu  21%user   4%nice   8%sys 764%idle   1%iow   2%irq   1%sirq   0%host
  PID USER         PR  NI VIRT  RES  SHR S[%CPU] %MEM     TIME+ ARGS
 5407 u0_a99       20   0 1.7G  69M  45M S 19.0   1.2  13:15.58 com.miui.voiceassist:voice_trigger
13736 shell        20   0  14M 3.0M 1.6M R  3.3   0.0   0:00.25 top
  704 audioserver  20   0  41M 6.3M 5.9M S  2.3   0.1   2:09.01 android.hardware.audio@2.0

其中

  • 800%cpu:即cpu核数,这里800%指有8个核。
  • 21%user:即用户态的占比占单核的21%
  • 4%nice:优先级为负数的进程占用的cpu
  • 8%sys:处于核心态的cpu占比
  • 764%idle:CPU处于空闲状态时间比例。一般而言,idel + sys + user + nice 约等于cpu
  • 1%iow:IO等待的CPU占比
  • 2%irq:硬中断的cpu占比
  • 1%sirq:软中断的cpu占比
  • PID:即进程id
  • PR:优先级,越低优先级越高。
  • VIRT:虚拟内存大小,包括:进程使用的库、代码、数据等。
  • RES:常驻内存,当前进程使用的内存大小,不包含swap out
  • SHR:除了自身进程的共享内存,也包括其他进程的共享内存。
  • %CPU:即该进程占用的CPU占比。
  • %MEM :即该进程占用的内存占比。

7、查看CPU——dumpsys

可以查看每个进程所用的CPU百分比

$ dumpsys cpuinfo
CPU usage from 476124ms to 176038ms ago (2021-10-17 13:35:57.070 to 2021-10-17 13:40:57.156):
  17% 5407/com.miui.voiceassist:voice_trigger: 16% user + 0.4% kernel / faults: 10382 minor
  2.6% 704/android.hardware.audio@2.0-service: 0.7% user + 1.9% kernel

更多dumpsys命令见android调试——教你用dumpsys命令调试

六、GPU

1、渲染速度

可以通过 设置-》开发者选项-》监控下的GPU呈现方式-》在GPU 渲染模式分析对话框中,选择在屏幕上显示为竖条

或者参考App性能调试详解 用命令打开。

// Possible values:
// "true", to enable profiling
// "visual_bars", to enable profiling and visualize the results on screen
// "false", to disable profiling
// @see #PROFILE_PROPERTY_VISUALIZE_BARS
adb shell setprop debug.hwui.profile #{value}

效果如下

其中, Android 6.0 及更高版本的设备时分析器输出中某个竖条的每个区段如下所示:

下表显示的是 Android 4.0 和 5.0 中的竖条区段。

2、过渡绘制

可通过 设置-》开发者选项-》硬件加速渲染-》调试 GPU 过度绘制-》选择显示过度绘制区域

或者使用命令打开

adb shell setprop debug.hwui.overdraw show

效果如下:

Android 将按如下方式为界面元素着色,以确定过度绘制的次数:

后记

到目前为止,Google一直为Android提供新的调试工具,从monitor(已经被打入冷宫)到profiler,从systrace到Perfetto,Android studio也一直在迭代更新。目的就是让开发者能开发出更优秀的产品,也愿各位大佬不辜负Google的期望!共勉!!

——Weiwq 于 2021.08 广州