(四十三)统计应用具体方法执行时长-Android Profiler CPU

前言:最近接触有些performance的故障,拿到手根本不知道如何下嘴,然后接触到了Android studio自带的Android Profiler,好好用,写个博客总结一下。


参考https://blog.csdn.net/niubitianping/article/details/72617864

test demohttps://github.com/happyjiatai/demo_csdn/tree/master/demo_43_androidprofiler_cpu


1. Android Profiler CPU简介

首先这个是Android studio自带的一个功能,打开方式:View > Tool Windows > Android Profiler


“CPU分析器可帮助您实时检查应用程序的CPU使用情况和线程活动,并记录方法跟踪,以便您可以优化和调试应用程序的代码。

优化CPU使用率有许多优点,例如提供更快更流畅的用户体验,并保持设备电池寿命。它还可以帮助您的应用程序在各种较新旧的设备上运行良好,您可以使用CPU分析器在与应用程序交互时监视CPU使用情况和线程活动,但是,有关应用程序执行代码的更详细信息,应记录并检查方法跟踪。

对于应用程序进程中的每个线程,您可以找到在一段时间内执行哪些方法以及每个方法在执行期间消耗的CPU资源。您还可以使用方法跟踪来识别调用者和被调用者,调用者是一种调用另一种方法的方法,被调用方是另一种方法调用的方法。您可以使用此信息来确定哪些方法太频繁地调用特定资源繁重的任务,就可以尝试优化应用程序的代码以避免不必要的工作。

如果要收集详细的系统级数据,帮助您检查本地系统进程并解决由丢帧引起的UI jank,则应使用Systrace。或者,如果要导出使用Debug捕获的.trace文件,则应使用Traceview


2. 简单使用

2.1 统计方法执行时长

2.1.1 test demo

先上一段test demo

package com.example.demo_42_androidprofiler_cpu;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = findViewById(R.id.button);
        OutMethod();
        btn.setOnClickListener(new View.OnClickListener() {

            /**
             * Called when a view has been clicked.
             *
             * @param v The view that was clicked.
             */
            @Override
            public void onClick(View v) {
                OutMethod();
            }
        });
    }

    private void OutMethod(){
        InnerMethod1();
        InnerMethod2();
        InnerMethod3();
    }

    private void InnerMethod1(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void InnerMethod2(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void InnerMethod3(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

可以观察到demo里写了一个OutMethod和3个innerMethod,耗时分别是1s/2s/3s

那么我们平常是怎么检测耗时的呢,都是写在代码前后然后减一下的,这样太不智能了,Android profiler CPU可以帮我们实现这项功能。


2.1.2 效果图

call chart:


flame chart:


Top down:


Bottom up:



2.1.3 测试方法

1)打开Android Profiler,选择监控的app包名,如下图所示


2)在CPU的区域点击一下,进入如下界面1处标注表示CPU监控界面,2表示我们监控的Sampled(java),也有native的可选,instrumented(java)暂时不知道是啥,参考文章及后面有讲。3处标注是一个开关,表示监控的开始和结束。


3)比如我们要监控test demo的主界面的加载耗时,这时候正好用到之前的冷启动和热启动知识,我们启动一下应用,然后点返回destroy掉主界面,但是应用已经被创建好了。下面是关键的啦

  1. 点一下第3处标注的红点,表示开始监控
  2. 手机点击test demo图标,应用进入热启动
  3. 应用界面加载完毕,点击第3处标注的红点结束监控

4)这时候就可以看到上面说的效果了,最后可以点击End Session结束啦。


2.2 查看activity的生命周期

旋转手机可以从如下界面看到activity的生命周期变化和旋转图标。


2.3 查看子线程方法耗时

上面只讲了主线程即UI线程的监控,那么子线程是否可以呢,当然可以的啦。

        new Thread("jiatai"){
            @Override
            public void run() {
                super.run();
                OutMethod();
            }
        }.start();
加上上面一句,新建一个叫做jiatai的子线程,监控结果如下:


Thread activity timeline: 列出属于您的应用程序进程的每个线程,并使用不同的颜色在时间轴上指示其活动。记录方法跟踪后,可以从此时间轴中选择一个线程,在跟踪窗格中检查其数据。

在 找到名为jiatai的线程,然后看到本次取样结束了早了,只采样到InnerMethod1和InnerMethod2,InnerMethod3没采样到,尴尬。



3. 基础知识转载

参考博客写的太好了,我要全抄过来=-=

3.1 CPU Profiler概述

当您打开CPU分析器时,它会立即开始显示应用程序的CPU使用情况和线程活动。你会看到类似于下图的内容 
image

如上图所示,CPU Profiler的默认视图包括以下内容:

  • ①Event timeline: 显示您的应用程序在其生命周期中转换不同状态的活动,并指示用户与设备的交互,包括屏幕旋转事件。要了解有关事件时间轴的更多信息,包括如何启用它,请阅读我上一篇文章说到的启用高级分析
  • ②CPU timeline: 显示您的应用程序的实时CPU使用率(占总可用CPU的百分比)以及应用程序使用的线程总数,时间轴还显示其他进程的CPU使用情况(如系统进程或其他应用程序),所以您可以将其与应用程序的使用情况进行比较。您可以通过沿着时间轴的水平轴移动鼠标来检查历史CPU使用率数据。
  • ③Thread activity timeline: 列出属于您的应用程序进程的每个线程,并使用不同的颜色在时间轴上指示其活动。记录方法跟踪后,可以从此时间轴中选择一个线程,在跟踪窗格中检查其数据。 
    • 绿色: 线程处于活动状态或准备好使用CPU。也就是说,它处于”运行”或”可运行”状态。
    • 黄色: 线程处于活动状态,但是在完成其工作之前,它正在等待I / O操作(如文件或网络I / O)。
    • 灰色: 线程正在睡眠,不会消耗任何CPU时间,当线程需要访问尚未可用的资源时,有时会发生这种情况。要么线程进入自愿性睡眠,要么内核使线程休眠,直到所需的资源可用。
  • ④Tracing type:允许您选择以下选项之一来确定分析器如何记录方法跟踪。 
    • Sampled: 在应用程序执行期间,您可以频繁地捕获应用程序的调用堆栈。profiler将捕获的数据集进行比较,以获取关于应用程序代码执行的时间和资源使用信息。基于sampled跟踪的一个固有问题是,如果您的应用程序在捕获调用堆栈并在下一次捕获之前退出该方法,那么该方法调用不会被分析器记录。如果您对具有这样短生命周期的跟踪方法感兴趣,您应该使用工具跟踪。
    • Instrumented: 在您的应用程序运行时记录每个方法调用的开始和结束时的时间戳。收集时间戳并与生成方法跟踪数据进行比较,包括时间信息和CPU使用。请注意,对每种方法进行检测的开销会影响运行时性能,并可能影响性能分析,因此对于具有相对较短的生命周期的方法来说,这更加值得注意。此外,如果您的应用程序在短时间内执行大量的方法,profiler可能很快超过它的文件大小限制,进而不能记录任何进一步的跟踪数据。
  • ⑤Record button:开始和停止记录方法跟踪。要了解更多信息,请继续看下去

    提示:profiler还报告了Android Studio和Android平台在你的应用程序过程中添加的线程的CPU使用情况,如JDWP、Profile Saver、Studio:VMStats、Studio:Perfa和Studio:Heartbeat(尽管,在线程活动时间线中显示的确切名称可能会有所不同)。这意味着您的应用程序在CPU时间轴上的CPU使用率也会报告这些线程使用的CPU时间。您可以在线程活动时间表中看到这些线程,并监视它们的活动。(但是,由于profiler线程执行native代码,因此无法为它们记录方法跟踪数据。)Android Studio会报告这些数据,这样你就可以很容易地识别出线程活动和CPU使用实际上是由你的应用程序代码引起的。

 3.2 记录和检查方法跟踪

要开始记录方法跟踪,从下拉菜单中选择SampledInstrumented类型,然后单击Record开始进行记录,完成后点击Stop recording停止记录。profiler自动选择记录的时间帧,并在方法跟踪窗格中显示它的跟踪信息,如下图所示。如果要检查不同线程的方法跟踪,只需从线程活动时间轴中选择它。 
image

  • ① Selected time frame: 在跟踪窗格中检查的记录时间框架的部分。当您第一次记录一个方法跟踪时,CPU分析器将自动选择您在CPU时间线中记录的整个长度。如果要检查仅记录的时间帧的一部分的方法跟踪数据,您可以单击并拖动高亮显示区域的边缘来修改它的长度。
  • ②Timestamp: 表示记录方法跟踪的开始和结束时间(相对于profiler开始从设备收集CPU使用信息时)。你可以点击时间戳来自动选择整个记录作为你选定的时间框架——如果你有多个你想要转换的记录,这是非常有用的。
  • ③Trace pane:显示您所选择的时间框架和线程的方法跟踪数据。仅当您记录至少一个方法跟踪后,此窗格才会显示。在此窗格中,您可以选择如何查看每个堆栈跟踪(使用跟踪选项卡)以及如何测量执行时间(使用时间参考下拉菜单)。
  • ④: 选择显示为Top Down tree, Bottom Up tree, Call Chart, or Flame Chart这些类型的图。您可以在下面的部分中了解有关每个跟踪窗格选项卡的更多信息。
  • 从下拉菜单中选择以下选项之一,以确定如何测量每个方法调用的时序信息: 
    • Wall clock time: 表示实际经过时间。
    • Thread time:计时信息表示实际的消耗时间减去不消耗CPU资源的那段时间的任何部分。对于任何给定的方法,它的线程时间总是小于或等于它的时钟时间。使用线程时间让您更好地了解给定方法所消耗的线程实际CPU使用量

3.3 使用Call Chart选项卡检查跟踪

Call Chart选项卡提供一个方法跟踪的图形表示,其中一个方法调用(或调用者)的周期和时间在水平轴上表示,而它的callees则显示在垂直轴上。对系统api的方法调用以橙色显示,调用您的应用程序自己的方法以绿色显示,方法调用第三方api(包括java语言api)以蓝色显示。下面的图显示了一个示例调用图,并说明了给定方法的自时间、子时间和总时间的概念。关于如何使用自上而下和自下而上检查痕迹的部分,请继续看下去

图3

提示: 如果想要跳转到方法的源代码,请右键单击该方法,然后选择Jump to Source。这可以从任何窗格选项卡工作。

3.4 使用火焰图表(Flame Chart)选项卡检查痕迹

火焰图选项卡提供了一个反向调用图表,聚合了相同的调用堆栈。也就是说,收集相同的调用序列的相同方法被收集并表示为火焰图中的一个较长的栏(而不是将它们显示为多个更短的条,如调用图所示)。这样就更容易看出哪些方法消耗的时间最多。然而,这也意味着横轴不再表示时间轴,相反,它表示每个方法执行的相对时间。

为了帮助说明这个概念,考虑下面图4中的调用图表。注意,方法D对B(B1、B2和B3)进行多次调用,其中一些调用B对C(C1和C3)进行调用。

image

因为B1、B2和B3共享相同的序列调用者(A→D→B)聚合,如下所示。同样,C1和C3聚合,因为它们共享相同的序列调用者(A→D→B→C)注意不包括C2,因为它有不同的调用者序列(A→D→C)。

image

聚合方法调用用于创建flame 图,如下图所示。注意,对于任何给定的方法调用,在flame图中,消耗最多CPU时间的callees首先出现。 
image

3.5 使用自上而下和自下而上检查

Top Down选项卡显示方法调用的列表,扩展方法节点显示其callees。下图显示了上面的图3中调用图的顶部向下图。图中的每个箭头都是从调用者到callee。

下图所示,在顶部的down选项卡中扩展方法A的节点将显示它的callees、方法B和D。在此之后,扩展方法D的节点将暴露它的callees、方法B和C,等等。与火焰图选项卡类似,顶部向下的树聚合跟踪信息,用于共享相同调用堆栈的相同方法。也就是说,火焰图标签提供了顶部下标签的图形表示。

Top Down选项卡提供以下信息,以帮助描述在每个方法调用上花费的CPU时间(在选定的时间段内,时间也代表线程总时间的百分比):

  • Self:方法调用用于执行自己的代码而不是它的callees的时间量,如上面的图3所示。
  • Children:方法调用花费的时间用于执行其被调用者,而不是其自己的代码,如图3中的方法D所示。
  • Total:方法的Self和Children的时间的总和。这表示应用程序执行方法调用的总时间量,如图3所示的方法D。 
    image

Bottom Up选项卡显示一个方法调用列表,扩展方法的节点显示其调用者。使用上图所示的例子中,下图提供了一个自下而上方法C .在自下而上的树中打开方法C的节点,显示每个独特的调用者,方法B和d .注意,虽然B两次调用C,B当扩大节点只出现一次自下而上方法C的树。再此之后,展开节点B显示其调用者方法A和D.

image

Bottom Up选项卡对于那些消耗最多(或最少)CPU时间的方法的排序方法很有用。您可以检查每个节点,以确定哪些调用者在调用这些方法上花费最多的CPU时间。与上面的树相比,底部树中每个方法的定时信息都是在每棵树的顶部(顶部节点)的方法。在记录期间,CPU时间也被表示为线程总时间的百分比。下表有助于解释如何解释顶级节点及其调用方方法(子节点)的定时信息。

名称 Self Children Total
自下而上树顶部的方法(顶层节点) 表示用于执行其自己的代码而不是其callees的方法的总时间。与上面的树相比,这个时间信息表示在记录期间对该方法的所有调用的总和。 表示用于执行callees而不是自己的代码的总时间。与上面的树相比,这个时间信息表示在记录期间对该方法的callees调用的所有调用的总和。 Self时间和Children的时间总和
Caller 方法 (子节点) 表示调用者调用callee的总时间。使用上图中的底向上树作为例子,方法B的自我时间将等于每个方法C调用时的Self时间的总和。 表示调用者调用的callee的总子时间。在上图中使用底部向上的树为例,方法B的孩子时间将等于每个方法C调用时执行方法C的总和。 Self时间和Children的时间总和

对于给定的记录,当profiler达到文件大小限制时,Android Studio停止收集新数据(但是这并没有停止记录)。这种情况在执行检测跟踪时通常会发生得更快,因为这种类型的跟踪会在较短的时间内收集更多的数据,而不是取样跟踪。如果将检查时间帧扩展到在到达限制后发生的记录期间,那么跟踪窗格中的计时数据不会发生变化(因为没有可用的新数据)。此外,当您只选择没有可用数据的记录的部分时,跟踪窗格将显示NaN用于计时信息。


4. 总结

以后监控自己写的app 方法耗时有了Android profiler cpu这个大杀器再也不用愁啦。

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/80584621