Android性能优化之CPU Profiler

作者 : 开心源码 本文共5766个字,预计阅读时间需要15分钟 发布时间: 2022-05-12 共113人阅读

低性能的APP常见的体现有启动/界面切换慢、动画掉帧、卡慢、耗电,甚至出现应用无响应、程序崩溃的现象。当我们着手处理这些性能问题时,面对的第一个问题就是需要找到合适的工具来检测这些问题,用肉眼观察来判断定位这类问题是不靠谱的。理想的检测工具要能做到两点:

  1. 一是可以定性的告诉我们应用能否有低性能问题,并且能定位到的点,指出哪个逻辑哪个方法使用系统资源低效,以便我们针对具体的问题给出对应的优化方案;
  2. 二是能定量说明问题点的严重程度,有具体的数字来衡量,这样我们比照优化前后的测量数据,即可以清晰地看到优化方案的收益和效果。

Android Profiler

为了满足上面两个需求,我们需要使用到systraceCPU Profiler这两个检测工具。

工具一:systrace
这个工具的作用从名字‘system trace’系统跟踪就能看出一二。我们主要是通过这个工具来记录分析系统级函数的执行情况。它从android内核收集CPU调度、存储器访问、应用线程等信息,生成一张html格式的报表。这个工具还会自动分析这些数据,高亮警示开发者注意这些可能有问题的地方。比方下图UI掉帧的问题。但是systrace只提供了系统方法调用信息,没有指明具体是由于调用我们APP程序哪个方法导致的,从systrace中只能定位出大概是在首个Activity创立的时候发生的。想要获取关于APP执行的详细信息就得使用今天的主角CPU Profiler了。

systrace

点击查看systrace更多介绍。

工具二:CPU Profiler
CPU Profiler 可实时检查应用的 CPU 使用率和线程运行情况,并记录函数跟踪,以便我们优化和调试相关代码。简单点说就是我们可以通过CPU Profiler查看应用在某段时间里某个线程执行了哪些方法,并且还定量的展现了执行这些方法所耗费的时间及其方法的调用堆栈。稍有经验的程序员都知道应用启动慢、界面切换慢、动画不流畅卡慢等相似问题基本都是UI刷新不及时的体现,UI刷新不及时就是由于UI线程被其余逻辑方法长时间占用导致。呐,CPU Profiler简直就是为理解决这个问题而生的,轻松的分析出在卡慢(或者者其余)过程中主线程都执行了哪些耗时操作。

Google官方提供的Android开发工具Android Studio附带了很多开发调试工具,这其中就包括了一系列的性能分析工具。在Android Studio 3.0之前这些工具叫Android Monitor tools,Android Studio 3.0开始成为Android Profiler tools。我现在使用的Android Studio版本是3.2.1,这篇文章主要详情的是3.2.1版本Android Profiler tools中的CPU Profiler – CPU分析器。下面我们来看下CPU Profiler的使用方法。

一、CPU Profiler概览

1、打开CPU Profile界面
  1. 点击 View > Tool Windows > Android Profiler(也可以点击工具栏中的 Android Profiler )。
  2. 在 Android Profiler 工具栏中点击“+”号选择您想要分析的设施和应用进程。 假如您通过 USB 连接了某个设施但该设施未在设施列表中列出,请确保您已启用 USB 调试。
  3. 点击 CPU 时间线中的任意位置就可打开 CPU Profiler。Android Profiler 界面是CPU、内存、网络、电量4项性能数据共享时间线视图,此共享时间线视图只显示时间线图表。 要详细分析,需要点击性能数据对应的图表,打开介绍视图。

Android Profiler 共享时间线视图

打开 CPU Profiler 后,可以看到相似下图的少量内容。它将立即开始显示应用的 CPU 使用率和线程 Activity。

CPU Profiler

  1. Event 时间线: 显示应用中在其生命周期不同状态间转换的 Activity,并表明客户与设施的交互,包括触摸事件、按键点击的事件。 如需理解有关 Event 时间线的更多信息,包括如何启用它,请阅读 启用高级分析。

  2. CPU 时间线: 显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用使用的总线程数。 此时间线还显示其余进程的 CPU 使用率(如系统进程或者其余应用),以便您可以将其与您的应用使用率进行比照。 通过沿时间线的水平轴移动鼠标,您还可以检查历史 CPU 使用率数据。

  3. 线程活动时间线: 列出属于应用进程的每个线程并使用下面列出的颜色沿时间线标示它们的状态。 在记录一个函数跟踪后,可以从此时间线中选择一个线程以在跟踪窗格中检查其数据。

    • 绿色区域: 表示线程处于活动状态或者准备使用 CPU。 即,它正在“运行中”或者处于“可运行”状态。
    • 黄色区域: 表示线程处于活动状态,但它正在等待一个 I/O 操作(如磁盘或者网络 I/O),而后才能完成它的工作。
    • 灰色区域: 表示线程正在休眠且没有消耗任何 CPU 时间。 当线程需要访问尚不可用的资源时偶尔会发生这种情况。 线程进入自主休眠或者内核将此线程置于休眠状态,直到所需的资源可用。
  4. Record按钮:现在当我们与应用交互时,可以通过 CPU Profiler 监控 CPU 使用率和线程状态了。 不过,如想要理解应用执行代码的详细信息, 我们需要记录和检查函数跟踪。

2、记录和检查函数跟踪

要开始记录函数跟踪,从下拉菜单中选择 Sampled 或者 Instrumented 记录配置,或者选择您创立的自己设置记录配置,而后点击 Record 。 与应用交互并在完成后点击 Stop recording。 分析器将自动选择记录的时间范围,并默认在函数跟踪窗格中显示主线程函数跟踪信息,如下图所示。假如您想检查另一个线程的函数跟踪,只要从线程活动时间线中选中它,函数跟踪窗格就会切换成所选线程的函数跟踪信息。

跟踪函数

  1. 选择时间范围: 用于确定您要在跟踪窗格中检查所记录时间范围的哪一部分。 当您初次记录函数跟踪时,CPU Profiler 将在 CPU 时间线中自动选择您的记录的完整长度。 假如您想仅检查所记录时间范围一小部分的函数跟踪数据,您可以点击并拖动突出显示的区域边缘以修改其长度。
  2. 时间戳: 用于表示所记录函数跟踪的开始和结束时间(相对于分析器从设施开始收集 CPU 使用率信息的时间)。 在选择时间范围时,您可以点击时间戳以自动选择完整记录,假如您有多个要进行切换的记录,则此做法尤其有用。
  3. 跟踪窗格: 用于显示您所选的时间范围和线程的函数跟踪数据。 仅在您至少记录一个函数跟踪后此窗格才会显示。 在此窗格中,您可以选择想如何查看每个堆叠追踪(使用跟踪标签),以及如何测量执行时间(使用时间引用下拉菜单)。
  4. 选择后,可通过 Top Down 树、Bottom Up 树、调用图表或者火焰图的形式显示您的函数跟踪。 您可以在下文中理解每个跟踪窗格标签的更多信息。
  5. 从下拉菜单中选择以下选项之一,以确定如何测量每个函数调用的时间信息:
    • Wall clock time:壁钟时间表示实际经过的时间。
    • Thread time:线程时间 = 实际经过的时间 – 线程未消耗 CPU 资源的时间。 对于任何给定函数,其线程时间始终少于或者等于其壁钟时间。 线程时间可以更好地理解到线程的实际 CPU 使用率中有多少是给定函数消耗的。

二、CPU Profiler 使用示例

上面把 CPU Profiler 工具的基本界面详情了一下。下面我们通过两个例子来进一步理解下 CPU Profiler 的用法。

示例1:界面切换卡慢

问题:初次打开播放页时界面卡慢,待优化。
分析:界面卡慢,应该数主线有耗时操作导致UI刷新不及时。所以我们是用CPU Profiler来定位问题。

  1. AS(Android Studio)连接上手机,手机上运行要分析APP来到MainActivity,再在AS的Profiler窗口选择要调试的进程,打开CPU Profiler。
  2. 而后在AS上点击 Record ,再在手机上操作跳转到PlayerActivity完成后点击 Stop recording。得到如下图所示信息。
  3. 仔细查看主线程Call Chart信息,发现播放页的onCreate()里的MultiscreenManager.init();耗用了绝大多数时间。仔细查看这一方法作用是初始化投屏相关功能。(初始话方法的具体逻辑去掉了,调用了sleep方法来模拟)

Call Chart 标签提供函数跟踪的图形表示形式,其中,水平轴表示函数耗费的时间,垂直轴显示其被调用者。 对系统 API 的函数调用显示为橙色,对应用自有函数的调用显示为绿色,对第三方 API(包括 Java 语言 API)的函数调用显示为蓝色。

播放页启动函数调用图

处理方案:问题找到理解决就很容易了,投屏功能不是播放页启动的必需初始化项目,但是假如后置到客户点击投屏功能以后再去初始化投屏SDK,则会影响投屏功能的客户体验。这种问题有一个国际标准处理方案,把该任务增加到闲时任务系统中去。

protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_player);    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {        @Override        public boolean queueIdle() {            MultiscreenManager.init();            return false;        }    });}

CPU Profiler 检测时测量的函数耗时会大于真正线上包运行的时间。一方面是函数跟踪工具会加入额外的计时逻辑,另一方面它还会关闭虚拟机的JIT功能。所以我们一般是比照优化前后测量的两组数据来判断优化的效果。

修改后再测试下数据,onCreate()方法耗时显著降低,投屏sdk初始化逻辑放在主线程空闲的时候去执行的。还有一点注意下,可能出现从播放页打开到客户请求投屏时,主线程一直不空闲,也就是我们的初始化任务没有执行情况。所以加到闲时任务系统中的任务还得具有一个特点,就是假如显示任务没有执行,后续逻辑也要能正常运行。不过这个问题也很容易处理,就是在后续逻辑执行前先判断下能否初始化了。

优化后数据

示例2:应用冷启动慢

问题:杀掉进程后,再次启动应用慢,待优化。
分析:有了上面的经验,分析这个问题也一样,先打开APP,选择要调试的进程,点击record按钮跟踪卡慢过程中函数调用信息。好了,问题来了,我们这里是要抓取应用冷启动过程函数调用信息,但是要用CPU Profiler工具抓取信息得先指定进程。应用启动前又没有进程信息可以指定。愁,挠头,程序员的头就是这么给挠秃顶的。

这个时候就该Debug 类出场了。我们可使用 Debug 类准确地控制设施何时开始和中止记录函数跟踪信息,来生成一份函数跟踪信息文件。而后再使用 Android Studio 或者 Traceview 查看各个跟踪日志。

Traceview 有点过时了。假如我们使用的是3.2及其升级的Android Studio,就没有必要用Traceview了。

在开始生成跟踪日志之前,要确保应用有权限写入外部存储WRITE_EXTERNAL_STORAGE,以便将跟踪日志保存至该设施。创立跟踪日志,在想系统开始记录跟踪数据的位置调用 Debug.startMethodTracing(),要中止跟踪的位置请调用 Debug.stopMethodTracing()。系统将在getExternalFilesDir() 目录下生成 .trace 文件,一般都在 ~/sdcard/Android/data/$packname/files 目录中。用Android Studio的Device File Explorer工具找到这个文件双击就可打开。

public class DymApplication extends Application {    @Override    public void onCreate() {        super.onCreate();                SimpleDateFormat date = new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");        String logDate = date.format(new Date());                // 在Application创立的时候开始函数跟踪        // 传入的参数是函数跟踪信息文件名,加时间戳保证文件不会被覆盖        Debug.startMethodTracing("sample-" + logDate);    }}public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main_activity);        try {            // TODO: 2018/12/7 怎样又是这个倒霉的sleep,为了测试APP启动卡慢问题            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        // 在首页Activity创立完成中止函数跟踪        Debug.stopMethodTracing();    }}

函数跟踪日志生成了,分析就和之前没什么两样了。

CPU Profiler 中还有少量信息也是很有用的,比方Top Down 和 Bottom Up ,这里没有讲到,大家可以自己去看下,有什么不明白的,也可以在下面留言探讨。

三、结束

总的来说就是 CPU Profiler 可以让我们查看应用进程中的每个线程,某段时间内执行了哪些函数,以及在其执行期间每个函数消耗的 CPU 资源(文章中耗时只得就是占用CPU的时间)。 还可以使用函数跟踪来识别调用方被调用方。 据此可以确定哪些函数负责调用常常会消耗大量特定资源的任务,并尝试优化应用代码以避免不必要的开支。

大家努力,最大限度减少应用的 CPU 使用率,向德芙巧克力一样,在各种新旧设施上都能提供纵享丝滑的客户体验。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android性能优化之CPU Profiler

发表回复