近期读到《Speed up your app》一文。这是一篇关于Android APP性能分析、优化的文章。在这篇文章中,作者介绍他的APP分析优化规则、使用的工具和方法。我觉得值得大家借鉴。英文好的读者可读原文(链接:http://blog.udinic.com/2015/09/15/speed-up-your-app)。

1、作者的规则

作者每次着手处理或寻找性能问题时,遵循下列规则:

  • 时常检测

在更新APP前后,用测试工具软件多检测几次APP性能,可快速得到测试数据。这些数字是不会说谎的。而仅仅用眼睛观察APP性能是肯定不行的。如你在观察几次相同的动画效果后,你就想象它运行的够快了,从而忽略一下问题。

  • 使用低端设备

如今硬件性能在不断的提升,如果仅在最新设备运行APP,可能不能充分暴露出APP中存在的性能问题。另外,尽管用户设备换手率已经很高了,但仍然不是所有用户都是使用最新的和最强功能的设备。所以应该使用低端设备上,运行APP,这可以帮助你更有效地发现问题。

  • 权衡

性能优化是要综合各方面因素进行评判、权衡。因为优化一项性能可能是要以牺牲另一项性能为代价的。分析、查找、修复是要花费很多时间。你要准备好自我牺牲。

2、作者的分析方法及使用工具

作者采用自顶向下方法,从手机运行的概况开始,逐级深入分析:方法的性能、内存使用情况、GPU渲染效果、视图层次、图形叠加绘制、图像透明值;解释Honeycomb引入的硬件加速以及视图层。

2.1 Systrace

手机实际就是一台功能强大的计算机,同时间做很多事情。Systrace能够展示手机运行的概况。

作者在Systrace中选取一个Alert,做为例子,讲解分析发现问题的方法:

1)由Alert找到函数(如:long View#draw()),再展开“Inflation during ListView recycling”

2)可以查看到函数的耗时,更详细观察分析其中哪项花费时间较长。

3)选择一帧查看它花费多久时间。如有一帧绘制用时超过19ms。展开“Scheduling delay”

4)它的值(Wall duration和CPU duration之间的差异)表明有很长时间CPU没有安排这个线程。

这就需要查查在整个这段时间里CPU都做了什么?但是Systrace只能查看运行概况,还不能得到更深层次的分析。为了找到CPU运行繁忙的真正原因,作者使用另一个工具:Traceview。

2.2 Traceview

Traceview是性能分析工具,可以显示每个方法运行时间。可从Android Device Monitor中启动,也可从代码中启动。

作者以“滚屏”动作为例说明Traceview分析方法。在“滚屏”动作的跟踪记录中,找到getView()方法,发现它被调用12次,每次CPU用时约3ms。但是每次完成整个调用却用时162ms!这就是个问题!

作者就继续查看getView()的子方法,查看各个子方法所用时间在总时间中比例。他发现Thread.join()的Inclusive Real Time占用约98%。他顺藤摸瓜,找启动子方法的Thread.run()方法(它是创建一个新线程时所调用的方法),逐个跟着,直到找到“元凶”:BgService.doWork()方法。

另外,GC(Garbage Collector – 垃圾收集器)不定期运行清理不用的对象。GC的频繁运行也会使得APP运行慢下来。为此,作者的下一步就是针对内存进行分析。

2.3 内存分析(Memory Profiling)

Android Studio逐步在改善,有越来越多的工具可以帮助我们找到和分析性能问题。作者用其分析内存使用情况。

2.3.1 Heap dump

Heap Dump可以看到Heap中依据类名排序实例的直方图。每个都有分配对象的总数,实例的大小和留在内存中对象的大小。后者告诉我们这些实例释放后,能够释放多少内存。这可帮助我们识别出大的数据结构和对象关系。这些信息可以帮助我们构建更有效的数据结构,解开对象之间联系以减少内存驻留,最终尽可能的减少内存占用。

在分析中,可发现“内存泄漏”。解决方法就是要记得在活动即将被销毁时调用onDestory()方法删除引用。

内存泄漏和较大对象的heap空间占用,使得有效内存减少,频繁引发GC事件,试图释放更多的heap空间。这些GC事件占用CPU,降低了APP性能。如果有效的内存数量不足与满足APP,且heap空间不能在扩大,就引发 —— OutOfMemortException —— APP崩溃。

Eclipse MAT(Eclipse Memory Analyze Tool)也是不错的内存分析工具。

2.3.2 Allocation Tracker

Allocation Tracker可生成在跟踪期间内存分配给所有实例的情况报告,报告可按类分组或按方法分组。它以很好的可视化方式展示哪个实例所获得内存最多。

使用这些信息,可以找出分配大内存的方法,和可能频繁触发GC的事件。

2.3.3 General memory tips

作者给出一些技巧:

  • 枚举

一直是讨论性能的热门话题。枚举比普通常数占用更多的内存空间吗?是的。但这肯定是坏事吗?未必。如在编写代码库,需要强类型安全性,这就应该使用它。如果有一组可以归结在一起的常数,此时使用枚举也许不不合适。怎样决定,你需要权衡考虑。

  • Auto-boxing

是自动将原始数据类型转换为其对应的对象表示(如:int 到 Integer)。每次原始数据类型被“装箱”到对象,就会产生一个新的对象(我知道这令人震惊)。如果有许多这样的操作,那么GC就频繁地运行。由于将原始类型数据赋值到对象时,自动进行auto-boxing的,就很容易忽视它的数量。解决方案就是尽量使数据类型保持一致。如果要在整个APP中使用原始数据类型,就尽量避免在没有实际需要时进行Auto-boxing。使用内存分析工具可以找到许多对象是表示原始数据类型。也可以用Traceview寻找到Integer.valueOf(),Long.valueOf()等等。

  • HashMap与ArrayMap / Sparse*Array

在Auto-boxing相关问题中,使用HashMap时,就要求用对象作为键值。如果在APP中用原始int类型,那么在使用HashMap时就需要将int转化到Integer。这种情况也许就需要用SparesIntArray。如果在键值仍然需要对象类型的情况下,也可以改用ArrayMap类。它非常类似HashMap,只是在其内部工作方式不同,是以降低速度为代价,使用较少的内存。这两者占用内存都比HashMap小,但检索所花费的时间略高于HashMap。如果数据项少于1000,它们的运行时没有什么差别。

  • Context Awareness

Activity内存比较容易泄漏。由于它们保持UI的所有视图层次,占用大量的空间,所以它们的泄漏也是非常“昂贵的”。许多操作都要求Context对象,你发起Activity。假如引用被缓存,并且该对象的存活期要长于你的Activity,如果没有清理它的引用,你就产生了内存泄漏。

  • 避免非静态内部类(inner class)

创建一个非静态内部类,并实例化它,就创建对外部类的隐式引用。如果内部类实例需要的时间比外部类长,这外部类就要在内存中保留,即使它不再需要了。例如,在Activity类内部,创建一个扩展AsyncTask的非静态类,然后着手启动异步任务,在它运行时,销毁活动。该异步任务在其运行期间,都保持这一Activity运行。解决方案就是不要这样做。如果需要这样,就声明一个静态内部类。

2.4 GPU Profiling

Android Studio 1.4增加一项新功能:分析GPU渲染功能。作者详细讲解这一新功能的分析方法。

在GPU选项卡下,可以在屏幕上看到图形化显示的渲染每帧所花费的时间。图形中每条都表示被渲染的一帧。颜色表示进程的不同周期:

  • 绘画(蓝色)

表示View#onDraw()方法。那部分建立/更改DisplayList对象,然后转换成GPU能够理解的OpenGL命令。高的条形可能是视图复杂,而要求更多的时间绘制它们的显示列表,而许多视图在短时间内就失效了。

  • 准备(紫色)

在Lollipop中,加入另一个线程,以帮助UI线程渲染更快。这个线程叫:RenderThread。它的责任是转换显示列表为OpenGL命令,再发送给GPU。这样在渲染过程中,UI线程可以开始处理下一个帧。这时UI线程将所有资源传送给RenderThread。如果有许多资源要传递(如许多/繁重显示列表),这一步可能需要较长时间。

  • 处理(红色)

执行显示列表产生OpenGL命令。由于需要视图重绘,如果有许多/复杂显示列表要执行转换,这一步可能需要较长时间。当视图无效或是移动时,都要要重绘视图。

  • 执行(黄色)

发送OpenGL命令到GPU。由于CPU发送这些缓存的命令到GPU,并期待收回干净缓存,这就阻塞调用了。缓存数量有限,并且GPU也很忙 —— CPU会发现自己必须先等待缓存释放。因此,如果在这一步我们见高的条形,就可能意味着GPU在绘制UI时非常忙,这个绘制在短时间内太复杂了。

具体操作实例见原文。

2.5 Hierarchy Viewer

作者喜爱这个工具。他对许多开发者根本不使用这工具感到失望。

使用Hierarchy Viewer,可以完整地观察到屏幕视图层次和所有视图的属性。还可以导出主题(theme)数据,查看到每个样式的所有属性。但是,这只是在Hierarchy Viewer独立运行时,才能查看这些数据。而不可以从Android监控器中查看。

作者在设计布局和优化布局时使用这个工具。

作者认为有时间,可以对每张视图都测量以及它的所有子视图。颜色表示视图与树中其他视图的比较情况,很容易找出最薄弱的环节。由于我们可以预览视图,这样就可通过视图树,跟踪找出可删除的冗余步骤。这其中,对性能影响最大的,被称为Overdraw。

2.6 Overdraw

如果GPU需要在屏幕上绘制很多内容,绘制每帧都需要增加时间,这样执行周期就拉长了,在图形中以黄色表示。在一些图形上再叠加绘制,如在红色背景上绘制黄色按钮,这就发生Overdraw。这种情况下,GPU需要先绘制红色背景,再在其上绘制黄色按钮,Overdraw就不可避免了。如果有太多的Overdraw层,这就使得GPU超负荷运行,偏离16ms的目标。

设置“Debug GPU Overdraw”开发者选项,所有Overdraw的严重程度都以颜色表示出来。1~2倍的Overdraw算好的,甚至有些小的红色区也不坏。但是如果在屏幕上有许多红色,这就有问题了。但是都被红色覆盖。这就是问题了。作者建议这时仅用一种颜色设置背景来解决这个问题。

注意:默认主题声明一个全屏窗口背景颜色。如果有不透明布局的Activity覆盖在整个屏幕上,可以通过删除窗口的背景色消除这层Overdraw。

Hierarchy Viewer能够输出所有层次到PSD文件中,用Photoshop中打开。在Photoshop中研究不同的层就可展示布局中的所有Overdraw。删除冗余的Overdraw,努力性能提高到蓝色上。

2.7 Alpha

使用透明效果也会影响性能。为什么?

ImageView相互重叠。用setAlpha()设置alpha值,这将传递给所有的子视图,对帧缓冲区进行绘制。结果都重叠混在一起。幸好,OS有这个问题的解决方案。布局数据被复制到off-screen缓冲区,用alpha值对off-screen缓冲区进行处理后,再复制到帧缓冲区中。效果就好了些。但是,我们为此付出了代价:把“帧”缓冲区改为off-screen缓冲区,实际上增加了一个隐含的Overdraw层。OS就不知道处理了,所以默认情况下经常要进行复杂地操作。

不过还是有方法设置alpha值,避免off-screen缓冲区增加的复杂性:

  • TextView

用setTextColor()替代setAlpha()。使用文本颜色的alpha通道,就可直接用它来绘制的文本。

  • ImageView

用setImageAlpha()替代setAlpha()。理由同TextView。

  • Custom View

如果自定义视图不支持覆盖视图,这复杂行为是无关紧要的。可通过重写hasOverlappingRendering()方法,让其返回false,通知OS直接绘制自定义的视图。还可以通过重写onSetAlpha()方面,让其返回true,选择手动处理设置,各alpha值对应的操作。

2.8 Hardware Acceleration

在Honeycomb(蜂巢,Android 3.x)引入硬件加速后,在屏幕上渲染APP可以以新的绘制模型(http://developer.android.com/guide/topics/graphics/hardware-accel.html)进行。新模型引入DisplayList结构,记录视图渲染绘制命令。还有另一个很好的特性,时常被开发人员忽视或不正确地使用 — 视图层。

使用视图层,我们能够非屏幕缓冲区(如前面所见,应用alpha通道)渲染视图,并且能按照我们的意愿操控它。由于利用这一特性能够更快地绘制复杂动画视图,所以它主要用于动画。没有这些层次,在改变动画属性(如:x坐标、缩放、alpha值等等)后,动画视图将无效。对于复杂视图,这个无效效果都传递到所有子视图,且重绘的成本很高。在硬件支持下,使用视图层时,GPU会为视图创建纹理。有几个操作可以用于纹理,而不会破坏它,如:X / Y位置、旋转、alpha等等。所有都意味着在动画期间,可以在屏幕上绘制复杂动画视图,而完全不会破坏它。这使得动画更加顺畅。

提出在使用硬件层时需要记住几件事:

  • 清理视图。硬件层消耗有限存储元件的空间、GPU。所以仅在确实需要的时候使用(像动画),并在事后清理。
  • 如果在使用硬件层后,改变视图,硬件层无效,并在非屏幕缓冲区重绘视图。这会发生在改变那些无硬件层优化的属性上(迄今为止,仅优化:旋转、缩放、x/y、转换、轴移和alpha)。

3、其他资料

作者为说明性能分析、优化,准备很多代码来模拟情景。这些代码可以在Github代码库(https://github.com/Udinic/PerformanceDemo) 或是 Google Play(https://play.google.com/store/apps/details?id=com.udinic.perfdemo) 找到。他将不同的场景分别放到不同的Activity中,并它们编写文档,尽可能帮助理解使用这些Activity会遇到哪方面的问题。可以用工具和运行APP来阅读Activity的javadoc。

作者还推荐一些学习交流方法:

  1. 作者极力推荐大家观看YouTube上一组Android Performance Patterns视频(https://www.youtube.com/playlist?list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu),其中许多短视频来自Google,讲解不同性能主题。
  2. 加入Android Performance Patterns Google+ community(https://plus.google.com/communities/116342551728637785407),这里可以与包括Google人在内的其他人一起讨论性能,分享想法、文章,和提出问题。
  3. 更有趣的链接:
    • 学习Android图形架构(http://source.android.com/devices/graphics/architecture.html)是怎样工作的。这里有你需要知道的Android怎样渲染UI的一切,讲解不同的系统元素,如:SurfaceFlinger,以及它们之间怎样相互交互的。这篇文档很长,但是值得一读。
    • 关于Google IO 2012谈话(https://www.youtube.com/watch?v=Q8m9sHdyXnE),展示绘图模型的工作原理,和如何/为什么在UI渲染时会有一个Jank。
    • Android性能研讨会(https://www.parleys.com/tutorial/part-2-android-performance-workshop), 从Devoxx 2013开始,展示Android 4.4中绘图模型的一些优化,演示不同的性能优化工具(Systrace,Overdraw等等)。
    • 关于预防性优化(https://medium.com/google-developers/the-truth-about-preventative-optimizations-ccebadfd3eb5)的长篇文章,说明这为什么不同于提早(premature)优化。由于许多开发者认为影响不明显,所以没有优化他们的部分代码。要记住一点,积少成多就是一个大问题了。如果你有机会优化一小部分代码,那怕它可能微乎其微,也不要放弃优化。
    • Android的内存管理(https://www.youtube.com/watch?v=_CruQY55HOk)这是Google IO 2011的旧视频。它仍然很有意义。它展示了Android怎样管理APP的内存的,以及怎样用工具(如Eclipse MAT)找出问题。
    • Google工程师Romain Guy所做的案例分析(http://www.curious-creature.com/docs/android-performance-case-study-1.html),怎样优化Twitter客户端。在这个案例分析中,Romain展示了他怎样在APP中找到性能问题,以及他建议如何修改它们。在一篇回帖中,展示了在重新设计同一APP后产生的其他问题。

作者希望大家已获得足够资料和更强的自信。从今天开始优化自己的APP!

作者关于性能优化的演讲视频在这里:http://www.youtube.com/embed/v3DlGOQAIbw?color=white&theme=light

Android APP性能分析方法及工具的更多相关文章

  1. Atitit.提升软件Web应用程序 app性能的方法原理 h5 js java c# php python android .net

    Atitit.提升软件Web应用程序 app性能的方法原理 h5 js java c# php python android .net 1. 提升单例有能力的1 2. 减少工作数量2 2.1. 减少距 ...

  2. Android APP 性能优化的一些思考

    说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才 ...

  3. Android app 性能优化的思考--性能卡顿不好的原因在哪?

    说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才 ...

  4. 十大技巧优化Android App性能

    无论锤子还是茄子手机的不断冒出,Android系统的手机市场占有率目前来说还是最大的,因此基于Android开发的App数量也是很庞大的. 那么,如何能开发出更高性能的Android App?相信是软 ...

  5. 优化Android App性能?十大技巧必知!

    无论锤子还是茄子手机的不断冒出,Android系统的手机市场占有率目前来说还是最大的,因此基于Android开发的App数量也是很庞大的.那么,如何能开发出更高性能的Android App?相信是软件 ...

  6. 包建强的培训课程(9):Android App性能优化

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  7. android app性能优化大汇总

    这里根据网络上各位大神已经总结的知识内容做一个大汇总,作为记录,方便后续“温故知新”. 性能指标: (1)使用流畅度:  图片处理器每秒刷新的帧数(FPS),可用来指示页面是否平滑的渲染.高的帧率可以 ...

  8. Qt qml调试,qml性能分析和优化工具

    QML语言为qt推出的用于界面编程的语言. 1)如何在qt creator中进行调试qml: 以Qt Creator 4.6.2为例: 在qt creator的debug模式下,可以直接在qml中打断 ...

  9. Android App性能评测分析-流畅度篇

    1.前言 在手机App竞争越来越激烈的今天,Android App的各项性能特别是流畅度不如IOS,安卓基于java虚拟机运行,触控响应的延迟和卡顿比IOS系统严重得多.一些下拉上滑.双指缩放快速打字 ...

随机推荐

  1. go interface

    //所有的结构体 都实现了空接口 //接口类型转换 结构体变量 = 接口名(实现接口的结构体变量) 只能高级转为低级 就是转前的接口中方法在转后接口中都有实现 package main import ...

  2. Velocity中#set指令

    引用可以让模板设计者生成动态内容,而指令允许设计者真正的负责页面的展现和内容. 指令是以#开头,后面紧跟一个关键字,比如if,else,foreach等.而这个关键字,是可以被放在{}中间的.这是规范 ...

  3. Script 代码段

    script代码段 1.script代码段的执行 在Javascript代码中,可以使用script作为基本标识,script代码段在运行过程中是分段解析与执行的. 2.script代码段执行流程 在 ...

  4. Bridge实现

    网桥原理: 传统的中继器,如HUB,是一个单纯的物理层设备,它将每一个收到的数据包,在其所有的端口上广播,由接收主机来判断这个数据包是否是给自己的. 这样,网络资源被极大的浪费掉了. 网桥之所以不同于 ...

  5. android:ImageView 和ImageButton的区别

    1.继承不同: java.lang.Object ↳ android.view.View ↳android.widget.ImageView ↳ android.widget.ImageButton ...

  6. Nim Game 解答

    Question You are playing the following Nim Game with your friend: There is a heap of stones on the t ...

  7. Android菜鸟的成长笔记(9)——Intent与Intent Filter(下)

    原文:[置顶] Android菜鸟的成长笔记(9)——Intent与Intent Filter(下) 接着上一篇的内容,下面我们再来看看Intent的Data与Type属性. 一.Data属性与Typ ...

  8. sql server 2008 r2 数据库操作时提示 9002错误“事物日志已满”问题

    事务日志截断 若要避免数据库的事务日志被填满,例行备份至关重要.在简单恢复模式下,备份了数据库后会自动截断日志,而在完整恢复模式下,只有备份了事务日志后方才截断日志.但是,截断过程有时也可能发生延迟. ...

  9. Flutter: X Android license status unknown

    Flutter 环境检测问题 资料 windows cmd C:\Users\ajanuw>flutter doctor -v [√] Flutter (Channel stable, v1.0 ...

  10. opencv: 基本知识(二);

    1.cvArr* 与 cv::Mat之间的转换; cv::Mat ---- > cvArr* cv::Mat img; IplImage temp = IplImage(img); cvArr ...