Android - 性能优化经验分享

前言

性能优化的过程分两部分: 1.发现性能瓶颈 2.制定方案,解决性能问题android

解决性能问题的方案须要具体状况具体分析,并无彻底固定的路子,更多的是靠经验的积累,本文不作涉及。可是发现性能瓶颈确实有着固定的方法。本文主要介绍 如何找到性能瓶颈 。性能优化

如何找到性能瓶颈

经常使用的性能检测工具是traceview,集成于 Android Device Monitor 中。从Android Studio3.0开始, Android Device Monitor 被废弃,取而代之的是 Android Profiler ,其中提供了 Memory Prodiler 、CPU Profiler、Network Prodiler三大功能。bash

内存优化(包括内存泄漏)经常使用的是 MAT 或者 LeakCanary ,而 Memory Profiler 至关于将 MAT 的简化版功能集成到 AS 中。相对的在性能优化方面,CPU Profiler 至关于将 traceview 的功能集成到了 AS 中。 因此,使用AS3.0以前版本的,可使用traceview,而使用AS3.0之后版本的,除了traceview,还能够选择CPU Profiler。 若是想追踪系统进程的详细数据,以解决帧引发的界面卡顿等问题,可使用 systrace ,本文不作涉及。并发

#####阅读全文文末领取免费高阶Android学习资料及高清思惟脑图!app

traceview 使用方法

使用 traceview 须要首先使用 Debug 类进行 插桩 ,当应用执行到被插桩的代码时就会在手机sdcard中自动生成 .trace 文件,以后使用 traceview 或者 AS(3.0以上版本)打开文件便可。ide

1、插桩

插桩须要使用到 Debug 类,而且会在 sdcard 中生成 .trace 文件,因此你必须首先保证你的应用具备写外部存储( WRITE_EXTERNAL_STORAGE )的权限。函数

在想要跟踪的代码逻辑开头和结尾处分别插桩:工具

// Starts recording a trace log with the name you provide. For example, the
// following code tells the system to start recording a .trace file to the
// device with the name "sample.trace".Debug.startMethodTracing("sample");...
// The system begins buffering the generated trace data, until your
// application calls stopMethodTracing(), at which time it writes
// the buffered data to the output file.Debug.stopMethodTracing();
复制代码

生成的 .trace 文件会被保存在固定目录下,与 getExternalFilesDir() 返回的目录相同,即 /sdcard/Android/data/[YOUR_PACKAGE_NAME]/files 下。 请注意,若是您的应用在未更改跟踪日志名称的状况下再次调用 startMethodTracing(),则会覆盖已保存至设备的现有日志。若是但愿每次运行都保存至不一样的日志文件,可使用以下代码:性能

// Uses the SimpleDateFormat class to create a String with
// the current date and time.SimpleDateFormat date =
        new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");
String logDate = date.format(new Date());
// Applies the date and time to the name of the trace log.Debug.startMethodTracing(
        "sample-" + logDate);
复制代码

若是系统在您调用 stopMethodTracing() 以前达到最大缓冲值,则会中止跟踪并向管理中心发送通知。 开始和中止跟踪的函数在您的整个应用流程内均有效。 也就是说,您能够在 Activity 的 onCreate(Bundle)学习

函数中调用 startMethodTracing(),在 Activity 的 onDestroy() 函数中调用 stopMethodTracing()。

2、查看 .trace 文件

插好桩后,安装应用并运行被检测部分的功能,而后就能够经过 AS 或者 traceview 查看文件了。

使用 AS 查看

在AS中点击 View - Tool Windows - Android File Explorer 打开 Android File Explorer :

在 /sdcard/Android/data/[YOUR_PACKAGE_NAME]/files 下便可找到生成的 .trace 文件,双击文件便可打开。

将 .trace 文件保存至电脑,直接拖入AS窗口,也可直接打开该视图。

在打开的视图中,左上方能够选择想要查看的线程。能够查看监控期间指定线程运行了多久、执行了哪些方法、每一个方法执行了多久等等。

其中有4个名词须要解释一下:

  • Wall Clock Time:壁钟时间,表示实际通过的时间,即进入某个方法到退出该方法的时间,不考虑线程是活动仍是休眠状态。

  • Thread time:线程时间,表示实际通过的时间减去线程没有消耗 CPU 资源(处于休眠)的时间部分。 对于任何给定函数,其线程时间始终少于或等于其壁钟时间。 使用线程时间可让您更好地了解线程的实际 CPU 使用率中有多少是给定函数消耗的。

  • Inclusive Time:方法执行本身代码的时间 + 执行本身child方法的时间。

  • Exclusive Time:方法执行本身代码的时间。

使用 traceview 查看

要使用 traceview 查看,须要首先将 .trace 文件保存到电脑:

adb pull /sdcard/Android/data/[YOUR_PACKAGE_NAME]/files/sample.trace D:\Documents\sample.trace
复制代码

打开 Android Device Monitor 。AS3.0之前的版本,就是LogCat所在的窗口,再切换一下tab页便可。AS3.0之后,进入 android-sdk/tools/ 路径,运行如下命令:

monitor
复制代码

虽然 Android Device Monitor 的 DDMS 也有 File Explorer ,可是未 root 的手机,查看不到上述路径,所以只能将 .trace 文件保存到电脑查看。

在 Android Device Monitor 中,依次点击 File - Open File ,选择 .trace 文件路径便可打开:

内容与AS打开时相似,相差较大的主要是图标部分,没有AS的 Call Chart 直观形象。

其中也有4个概念:

1.Cpu Time:至关于AS中的 Thread time。 2.Real Time:至关于AS中的Wall Clock Time。 3.Inclusive Time:同AS同样。 4.Exclusive Time:同AS同样。

使用 AS 查看仍是使用 traceview 查看

这个就见仁见智了,根据我我的使用的感受来看,建议使用AS查看。缘由有二:

AS更简单。不须要单独打开ADM,更不须要将 .trace 文件保存到电脑。

AS的调用图( Call Chart )更加直观,cpu时间的消耗一目了然。

Call Chart 的水平轴表示函数调用(或调用方)的时间段和时间,并沿垂直轴显示其被调用者。 下图展现了一个调用图表示例,并描绘了给定函数的 self time、children time 以及总时间的概念。

最后须要注意一点,跟踪分析过程当中,应用的运行速度会减慢。因此,经过 traceview 获得的分析数据并不能精确反应某个方法在实际执行时的绝对时间。关于这一点,在最后的注意事项中再作详细分析。

Google还提供了基于样本的分析方式,以减小分析对运行时性能的影响。要启用样本分析,需调用 Debug.startMethodTracingSampling() 方法(而非 Debug.startMethodTracing() 方法)。系统会按期收集样本,直至调用 stopMethodTracing() 。

CPU Profiler 使用方法

使用 CPU Profiler 进行函数跟踪比 traceview 更简单。不须要作任何代码上的植入,下面作一个简单的介绍:

首先,经过

View - Tool Windows - Android Profiler 打开 Android Profiler 。手机链接电脑后运行应用,在 Android Profiler 中会看到如下视图:

左上角能够选择设备和进程,点击 CPU 区域,便可进入CPU Profiler视图:

左上角能够选择跟踪模式:

**Sampled:**按默认采样率捕获应用的调用堆栈。该模式的固有问题是,若是应用在一次捕获后进入一个函数并在下一次捕获前退出该函数,则分析器不会记录该函数调用。若是对此类生命周期很短的跟踪函数感兴趣,可使用“Instrumented”跟踪。

**Instrumented:**以在每一个函数调用的开始和结束时记录时间戳。 分析比较时间戳,以生成函数跟踪数据。 须要注意的是,设置与函数关联的开销会影响运行时性能,甚至分析数据,对于生命周期相对较短的函数,这一点更为明显。 此外,若是应用短期内执行大量函数,则分析器可能会迅速超出它的文件大小限制,且不能再记录更多跟踪数据。

**Edit configurations:**自定义采样率。与 traceview 中的 Debug.startMethodTracingSampling() 相似。

.trace 文件的大小是有限制的。对于给定录制,当分析器到达该限制时,AS 将中止收集新数据(不过,这不会中止记录)。 在执行“Instrumented”跟踪时,这种状况一般会更快发生,由于与“Sampled”跟踪相比,此类跟踪在较短期里会收集更多数据。

若是你使用的是Android 8.0(API 26)或更高版本的设备,则对于跟踪数据的文件大小没有限制,此值可忽略。不过,你仍需留意每次记录后设备收集了多少数据,由于 AS 可能难以解析大型跟踪文件。

点击上方的“开始录制”按钮,而后在应用中操做执行被追踪的功能,结束后再点击“中止录制”按钮。CPU Profiler 会自动开始分析并生成数据。

以上就是 CPU Profiler 和 traceview 的使用方法。至于如何制定优化方案,就不展开了,并无彻底固定的路子。就我本例的 onRebuild() 方法而言,是针对耗时的Contact构造过程作了并行处理,将上百个有序的构造过程平分到5个线程中并发执行,而后再按顺序合并数据到一个线程中。最终 onRebuild() 执行速度从15秒提高到了2.5秒,对我来讲已经够用了。

重要注意事项

不管是使用 traceview 仍是 CPU Profiler 进行函数跟踪,有一点须要注意:跟踪分析过程当中,应用的运行速度会减慢。因此,分析数据并不能精确反应某个方法在实际执行时的绝对时间。下面是我在优化项目中的 onRebuild(boolean) 方法时,记录的4组数据,让咱们来对比一下:

**实际执行时间:**不启用分析模式,正常运行状态下经过打印日志获得的实际执行时间。 **Profiler统计时间:**使用 CPU Profiler 分析得到的执行时间。 **traceview统计时间:**经过分析 traceview 产生的 .trace 文件,从中得到的执行时间。 **traceview实际时间:**使用 traceview 的状况下,经过打印日志获得的实际执行时间。

为何针对 traceview 会例举两个时间呢?这是由于测试过程当中发现 traceview 自动分析出来的时间比 实际执行时间 不只没有慢,反而快了不少,疑惑下又在启用 traceview 的状况下经过如下代码测算了一下实际的时间,这个却是真的比 实际执行时间 慢了。

Debug.startMethodTracing("smssdk_onrebuild");
long curr = System.currentTimeMillis();
onRebuild(true);
Log.d(TAG, "onRebuild lasts: " + (System.currentTimeMillis() - curr));
Debug.stopMethodTracing();
复制代码

从上表数据可见,不管是 CPU Profiler 仍是 traceview ,统计出来的时间都不能准确表明实际执行时间。更甚者,traceview自动分析出来的数据也与traceview跟踪模式下实际的时间有巨大差异,关于这一点,我没找到详细的解释,若是有人知道,还望不吝赐教。  既然跟踪分析获得的时间都不能表示实际的时间,那么这些数据是否是没用呢?固然不是!它们至少在如下两个方面具备价值:

  • 在一次检测获得的数据中,线程内各个方法执行所耗时间在整个线程执行时间中所占比例具备必定参考价值。占比高的方法当是优化的重点目标。

  • 优化先后两次检测获得的数据,有比较价值,以确认优化方案是否真的生效。

经过这些工具跟踪函数,也只能作一个相对的参考,并不能彻底正确的反应函数的执行性能。好比我经过 CPU Profiler 得到的 onRebuild() 方法的分析数据显示,整个执行过程当中 Contact 的构造方法占了60%左右,Contact.toString() 方法占了40%左右,但实际上在 onRebuild() 方法消耗的15秒中,Contact.toString() 只消耗了百毫秒级,而九成以上时间都被其构造方法消耗了,说明 CPU Profiler 的监控过程对 Contact.toString() 的性能产生了更大的影响。

而一样的问题却并无出如今 traceview 的分析结果中。

请注意,CPU Profiler 和 traceview 不能同时使用,若是代码中植入了插桩的代码,则有可能致使 CPU Profiler 没法正常开始或中止录制。

traceview 和 CPU Profiler 的对比

从用法上来看,traceview 比 CPU Profiler 稍微复杂一点。相似于MAT须要首先获取 .hprof 堆转储文件,traceview 也要首先获取 .trace 文件,而后使用traceview分析该文件。而 CPU Profiler 则能够直接对应用进行分析。

从最终生成的图表上来看,CPU Profiler 生成的图表有 Call Chart、Flame Chart ,它们能够很是形象的表示出线程内执行了哪些函数,函数的执行时间,调用栈等等,一目了然,并且在任意函数上点击右键,能够直接跳转至对应的代码,很是方便,在这一点上,相对于 traceview 要优秀。

本人Java开发4年Android开发5年,按期分享Android高级技术及经验分享,欢迎你们关注~

喜欢文章的点个赞叭~

相关文章
相关标签/搜索