在手机App竞争愈来愈激烈的今天,Android App的各项性能特别是流畅度不如IOS,安卓基于java虚拟机运行,触控响应的延迟和卡顿比IOS系统严重得多。一些下拉上滑、双指缩放快速打字等操做,安卓的流畅度都表现比较糟糕,可是,对于App使用过程是否流畅,一直没有一个可靠的指标将用户的客观感觉和数据一一对应。虽然以前有FPS(每秒帧数)做为游戏或视频类App的性能指标,但对于那些界面更新很少的App来讲,仍不是一个合适的衡量数据。如下会根据实际app性能测试案例,展开进行app性能评测之流畅度进行原理分析和评测总结。html
刷新率:每秒屏幕刷新次数,手机屏幕的刷新率是60HZ
帧率:GPU在一秒内绘制的帧数java
由于屏幕的刷新过程是自上而下、自左向右的,若是帧率>刷新率,当屏幕尚未刷新n-1帧的数据时,就开始生成第n帧的数据了,从上到下,覆盖第n-1帧。若是此时刷新屏幕,就会出现图像的上半部分是第n帧的,下半部分是第n帧的现象。CPU/GPU一直都在渲染。android
Android系统每隔16ms发出VSYNC信号,触发GPU对UI进行渲染,若是你的某个操做花费时间是24ms,系统在获得VSYNC信号的时候因为尚未准备好,就没法进行更新任何内容,那么用户在32ms内看到的会是同一帧画面(卡顿现象),即丢帧现象。git
GPU向缓存中写入数据,屏幕从缓存中读取数据,刷新后显示。因为刷新率和帧率并不老是一致的,极可能致使撕裂的现象。为了解决单缓存的画面撕裂问题,出现了双缓存和 VSync 。github
双缓存使用了两个缓存区: Back Buffer 、 Frame Buffer。当写入下一帧时,GPU会先填充 Back Buffer 中,当刷新屏幕时,屏幕从 Frame Buffer 中读数据。VSYNC 主要是完成帧的复制,开始下一帧的渲染。 shell
当帧率大于刷新频率时,经过使帧率被迫跟刷新频率保持同步,从而避免画面撕裂的现象(只有当 VSync 信号产生时, CPU/GPU 才会开始绘制)。当VSync 信号产生时,先完成Back Buffer 到 Frame Buffer的复制操做(经过交换内存地址),而后通知 CPU/GPU 绘制下一帧图像。也只有VSync 信号发生时,才绘制下一帧。数据库
当刷新频率>帧率时,此时刷新屏幕,发出VSYNC 信号,因为CPU/GPU的渲染操做尚未完成,就不把Back Buffer的数据复制到 Frame Buffer,此时就从Frame Buffer去取旧数据,这样在两个刷新周期里,显示的是同一帧数据。android-studio
双重缓存的缺陷在于:当 CPU/GPU 绘制一帧的时间超过 16 ms 时,会产生 Jank。更要命的是,产生 Jank 的那一帧的显示期间,GPU/CPU 都是在闲置的。
以下图,A、B 和 C 都是 Buffer。
若是有第三个 Buffer 能让 CPU/GPU 在这个时候继续工做,那就彻底能够避免第二个 Jank 的发生了!缓存
在Android版本更新过程当中,发如今Jelly Bean中Google加入了一个Project Butter,用来解决严重影响Android口碑的问题之一“UI流畅性差”的问题。
而Project Butter中主要引入了三个核心元素:VSYNC(垂直同步)、Triple Buffer和Choreographer。
VSync是VerticalSynchronization(垂直同步)的缩写,是一种在PC上很早就普遍使用的技术,能够简单的把它认为是一种定时中断。而在Android 4.1(JB)中已经开始引入VSync机制。CPU和GPU的处理时间都少于一个VSync的间隔,即16.6ms。若是每一个间隔都有绘制的状况下,当前的FPS即为60帧。VSync机制就像是播放动画片(60帧/s)。每次都会播放画面,有的时候有人偷懒了,机器坏了,就会出现播放速度下降的情况。咱们把这个播放速度叫作流畅度。性能优化
FPS即Frames per second,>>点击这篇文章,解释的很是清楚。
用过flash的人应该知道动画片实际上是由一张张画出来的图片连贯执行产生的效果,当一张张独立的图片切换速度足够快的时候,会欺骗咱们的眼睛,觉得这是连续的动做。反之类推,当你的图片切换不够快的时候,就会被人眼看穿,反馈给用户的就是所谓的卡顿现象。
想要让大脑以为动做是连续的,至少是每秒10-12帧的速度,而想达到流畅的效果,至少须要每秒24帧。这也是为何电影片源一般都是24帧的缘由,好奇的同窗点击>>知乎高知
看看大神的解答。不过60帧每秒的流畅度是最佳的,咱们的目标就是让程序的流畅度能接近60帧每秒,固然超过60帧速的话大部分人仍是会受不了的。
系统获取FPS的原理是:1s 内 SurfaceFLinger 提交到屏幕的帧数。
计算公式:1000ms / 60 frames ≈ 16.67 ms/frames
原来的测试产品的流畅度,FPS是一个重要的指标,可是用了一段时间后,人们就发现了这样两个问题
Question:
1)为何有时候FPS很低,可是咱们却不以为App卡顿?
2)App中止操做以后,FPS仍是一直在变化,这样的状况是否会影响FPS的准确度?
后来测试人员分析了系统获取FPS的原理后,找到了那两个问题的答案:
Answer:
1)有时候FPS很低,咱们却感受不到卡顿,由于原本就用不到那么高的FPS,好比画一个动画只画了0.5秒就画完了,那么FPS最高也只有30帧/秒(标准是60帧/每秒),但这并不表明它是卡顿的,用0.5秒动画就画完了,不能为了凑够60帧/秒,在作个1s的动画吧。而若是屏幕根本没有绘制需求,即屏幕显示的画面是静止的,那FPS就为0。
2)App中止操做后FPS还一直变化,是由于屏幕每一帧的合成都是针对手机里的全部进程,那么即便你的App中止了绘制,手机里其余进程可能还在绘制,好比通知栏的各类消息,这会致使FPS继续变化。
从上一节的原理分析来看,对流畅度的评价没有一个既定的测量标准。不一样的应用有相对适应的计算方式,总结以下:
系统合成帧率(FPS):数据形式最为直观(FPS 是最先的显示性能指标,并且在多个平台中都有着相似的定义),且对系统平台的要求最低(API level 1),游戏、视频等连续绘制的应用能够考虑选用,但不适用于绝大多数非连续绘制的应用;
流畅度(SM):数据形式与 FPS 相似,能够很好的弥补 FPS 没法准确刻画非连续绘制的应用显示性能的缺陷;
应用跳帧次数、幅度(Aggregate frame stats):除了对系统平台有较高的要求之外,其采集方式最为简单(系统自带功能);
丢帧(Skipped frames):与 Aggregate frame stats 相似, 信息量相对较少,但可适用范围更广
APP须要尽量的超过24帧/秒,接近60帧/秒的速度,而且在使用的过程当中保持这个速率,所以这意味着咱们的程序须要在16.67ms内处理一幅画面内的全部事,并保持住这个状态。
计算公式:1000ms / 60 frames ≈ 16.67 ms/frames
操做方法:经过 [设置]->[开发者选项]->[GPU呈现模式分析] ->[在屏幕上显示为条形图] 进行直观的取样,截图以下:
操做:设备链接usb数据线,使用adb调试工具,随后对返回的数据进行适当处理即可以获得此时此刻app的fps。adb shell dumpsys gfxinfo yourpackagename
解读:
Draw:是消耗在构建java显示列表DisplayList的时间。说白了就是执行每个View的onDraw方法,建立或者更新每个View的DisplayList对象的时间。
Process:表示是消耗在Android的2D渲染器执行显示列表的时间,view越多,要执行的绘图命令就越多,时间就越长
Execute:消耗在排列每一个发送过来的帧的顺序的时间.或者说是CPU告诉GPU渲染一帧的时间,这是一个阻塞调用,由于CPU会一直等待GPU发出接到命令的回复。因此这个时间,通常都很短。
Draw + Prepare+Process + Execute = 完整显示一帧 ,这个时间要小于16ms才能保存每秒60帧。
将数据复制到excel中,而后将数据生成“堆积柱形图”以下:
原理:在 Android 系统中,SurfaceFlinger 扮演了系统中全部 Surface 的管理者的角色,当应用程序所对应的 Surface 更新以后,绝大多数的 Surface 都将在 SurfaceFlinger 之中完成了合并的工做以后,最终才会在 Screen 上显示出来。
知道android绘制原理的人应该能明白,SurfaceFlinger就是负责绘制Android应用程序UI的服务,因此surfaceFlinger能反应出总体绘制状况,通常正常状况都是连续的,若是出现空档,一种是没有操做或者滑动到头,没东西须要绘制,这种属于正常,另外一种就是有问题存在,有其余操做时间过长。
操做:设备链接usb数据线,使用adb调试工具,adb shell dumpsys SurfaceFlinger packagename
首先须要说明的是 Aggregate frame stats 不是一个指标,而是一系列指标集合。咱们来看一个具体的 Aggregate frame stats 的例子:
Stats since: 752958278148ns
Total frames rendered: 82189
Janky frames: 35335 (42.99%)
90th percentile: 34ms
95th percentile: 42ms
99th percentile: 69ms
Number Missed Vsync: 4706
Number High input latency: 142
Number Slow UI thread: 17270
Number Slow bitmap uploads: 1542
Number Slow draw: 23342
以上统计信息的实现能够详见源码:GfxMonitorImpl.java
在 Android M 以上的系统上,上述信息的获取十分方便(事实上也只有这些系统可以获取这些信息)。仅须要执行如下命令便可:
adb shell dumpsys gfxinfo <PACKAGE_NAME>
优势:除了对系统平台有较高的要求之外,其采集方式最为简单(系统自带功能);
在一次Loop时若是执行时间超过了16.6ms,那么用多于16.6ms的时间除以16.6ms,便是当前App的丢帧(SF: Skipped Frame)
在16.6ms完成工做却因各类缘由没作完,占了后n个16.6ms的时间,至关于丢了n帧
故:
SF=处理帧数 / (处理帧数 + 额外的垂直同步脉冲) * 60 计算(其中处理帧数常为128)
这个指标的就是指当前应用在丢帧发生时的丢帧帧数。
针对 Logcat 方案, 该数值直接在 Logcat 中输出,而且带有时间信息。
04-18 16:31:24.957 I/Choreographer(24164): Skipped 4 frames! The application may be doing too much work on its main thread. 04-18 16:31:25.009 I/Choreographer(24164): Skipped 2 frames! The application may be doing too much work on its main thread.
针对 Choreographer.FrameCallback 方案 以及 代码注入方案,咱们可能很方便的经过计算先后两帧开始渲染的时间差得到这一数值,一样方便。一样与 Logcat 方案 不一样的是,它也是能够设计成实时计算的。
缺点:Android4.2+系统,适用于SW/HW Rendering 及 部分 OpenGL Rendering
原理:VSync 机制就像一台转速固定的发动机(60转/s),每一转带动着作一些 UI 相关的事情。有时候由于各类阻力, 某一圈的工做量比较重, 超过了 16.6ms, 那么这一秒内就不是 60 转了。
咱们经过测量这个转速,来评判应用的流畅度。
和丢帧相对,在VSync机制中1s内Loop运行的次数。和丢帧相对1s内有60个Loop由于某几回工做时间超过了16.6ms(丢帧),这样Loop就没法运行60次(理论最大值)。当流畅度越小的时候说明当前程序越卡顿。
计算方式:SM = 帧率(60) * (单位时长总帧数 - 单位时长丢帧数) / 单位时长总帧数
操做:
VSync机制客户经过其Loop来了解当前App最高绘制能力,其机制以下:
1)固定每隔16.6ms执行一次;
2)若是没有绘制事件的时候也会运行这样一个Loop;
3)Loop在1s以内运行了多少次,便可以表示当前App绘制的最高能力,也就是App卡顿的程度。
if(存在帧的绘制):
Loop = 1 帧绘制完成所占用的Vsync间隔
else: Loop = 1个Vsync间隔
因此SM计算方法为Loop在1s内运行了多少次(Loops per seconds),那么咱们能够直接在App代码中经过Choreographer的回调FrameCallback来计算Loop被运行了几回,从而知道应用的流畅度。但在实际状况下咱们不必定能修改代码(实际发布的版本不容许加入测试代码)或者根本拿不到代码(譬如和竞品进行对比)。
因此介绍一种更简单直观测量Android应用流畅度的方法,就是经过开源测试工具GT(http://gt.qq.com)。
优势:数据形式与 FPS 相似,能够很好的弥补 FPS 没法准确刻画非连续绘制的应用显示性能的缺陷;
缺点:Android4.2+系统,适用于SW/HW Rendering 及 部分 OpenGL Rendering
分析UI卡顿咱们通常都借助工具,经过工具通常均可以直观的分析出问题缘由,从而反推寻求优化方案,具体以下细说各类强大的工具
咱们能够经过SDK提供的工具HierarchyViewer来进行UI布局复杂程度及冗余等分析
经过命令启动HierarchyViewer
Hierarchyviewer
接下来Hierarchy window窗口打开:
一个Activity的View树,经过这个树能够分析出View嵌套的冗余层级,以及每一个View在绘制的使用时长也有表示。
冗余资源及逻辑等也可能会致使加载和执行缓慢,这可使用Link工具,发现代码中的流畅度性能问题;
在AndroidStudio 1.4版本中使用Lint最简单的办法:就是将鼠标放在代码区点击右键->Analyze->Inspect Code–>界面选择你要检测的模块->点击确认开始检测,等待一下后会发现以下结果:
若是存在冗余的UI层级嵌套,会进行高亮显示, 咱们根据提示能够点击跳进去进行优化处理掉的。
因为Android系统会依据内存中不一样的内存数据类型分别执行不一样的GC操做,常见应用开发中致使GC频繁执行的缘由主要多是由于短期内有大量频繁的对象建立与释放操做,也就是俗称的内存抖动现象,或者短期内已经存在大量内存暂用介于阈值边缘,接着每当有新对象建立时都会致使超越阈值触发GC操做
根据内存抖动现象,查看log日志进行分析:
若是看到,这种不停的大面积打印GC致使全部线程暂停的操做一定会致使UI视觉的卡顿,因此咱们要避免此类问题的出现,具体的常见优化方式以下:
检查代码,尽可能避免有些频繁触发的逻辑方法中存在大量对象分配;
尽可能避免在屡次for循环中频繁分配对象;
避免在自定义View的onDraw()方法中执行复杂的操做及建立对象(譬如Paint的实例化操做不要写在onDraw()方法中等);
对于并发下载等相似逻辑的实现尽可能避免屡次建立线程对象,而是交给线程池处理。
有了上面说明GC致使的性能后咱们就该定位分析问题了,咱们能够经过运行DDMS->Allocation Tracker标签打开一个新窗口,而后点击Start Tracing按钮,接着运行你想分析的代码,运行完毕后点击GetAllocations按钮就可以看见一个已分配对象的列表,以下:
能够记录和分析APP每一帧的绘制过程,以及列出全部乃至的OpenGL ES 的绘制函数和耗时;该工具操做后会生成一份记录App绘制过程和gltrace文件。
在复杂的项目环境中,因为历史代码庞大,业务复杂,包含各类第三方库,因此在出现了卡顿的时候,咱们很难定位究竟是哪里出现了问题,即使知道是哪个Activity/Fragment,也仍然须要进去里面一行一行看,动辄数千行的类再加上跳来跳去调来调去的,结果就是不了了之随它去了,实在不行了再优化吧。因而一拖再拖,最后可能压根就改不动了,客户端愈来愈卡。
Android应用卡顿是很是广泛的现象,偶尔出现ANR。只有当APP出现ANR,咱们才能获得当前堆栈信息。当应用只是卡顿或只是不太流畅的时候,咱们能不能找出卡顿元凶呢?不依赖Debug和源码的状况,能不能找出卡顿的堆栈信息呢?咱们须要找到一种方法来检测哪些函数可能会使应用发生ANR,在开发阶段就能找出卡顿元凶,提升应用流畅度。
BlockCanary就是来解决这个问题的。告别打点,告别Debug,哪里卡顿,一目了然,让优化代码变得有的放矢。
具体使用方法请点击:
这次质量开放平台-评测中心(http://fit-stg1.jryzt.com/Hyperion-server/html/index.html)的性能测试的流畅度测试主要是针对场景页面的掉帧率数据采集进行对比分析, 原理公式为:掉帧率=处理帧数 / (处理帧数 + 额外的垂直同步脉冲) * 60 计算(其中处理帧数常为128)。通常掉帧率超过10%,咱们就认为存在卡顿有必要进行分析定位。
这里选取了同一家银行的两个APP与行业竞品进行掉帧率对比分析,从掉帧率对比看,行业竞品均值为4.1%,90分位约13.1%,75分位约27.5%,中位数约39.6%,25分位约59.9%。
【福建农信】掉帧率为1.783%,表现良好,战胜了行业90%以上的竞品
【榕商Bank】掉帧率为6.244%,表现良好,战胜了行业90%以上的竞品
总体得分对比分析:
从首页启动到加载完成场景分析,【福建农信】实际启动到首页场景只有一个简单的未登陆页,相比于丰富多样的【榕商Bank】来讲属于很是简单的页面,可是它的掉帧率与丰富资源的【榕商Bank】比较相差不远。
【福建农信】首页掉帧率问题分析:
单纯从页面表象观察,【福建农信】启动时,未登陆页是从APP背景页下方飘进渐渐上升在页面中间,而后抖动一下再静止,有一种PPT飞入的动态效果。
经过深刻分析得出【福建农信】应用交互中主线程存在卡顿,存在 Activity(LoginActivity)切换过慢的现象:![]()
cn.com.fjnx.mobilebank.per.activity.account.LoginActivity.onCreate(阻塞1639 ms)
com.yitong.fjnx.mbank.android.Splash.onCreate(阻塞1717 ms)
建议【福建农信】优化启动时未登陆页进入的方式
【榕商Bank】首页掉帧率问题分析:
首页加载掉帧率为8.2%,经过GPU过分绘制调试发现:com.pingan.fstandard.activity.MainActivity存在过分绘制。其实是由于运营Banner位有轮播动态效果,轮播间隔时间设置的比较长,致使评测时掉帧率偏高,可是这是合理的产品设计,并且也不影响用户体验。
综上对比,【榕商Bank】流畅度表现优于【福建农信】,【福建农信】掉帧率仍然有优化空间。
问题:UI线程中有I/O读写、数据库访问等耗时操做,致使UI线程卡顿;
定位及解决:TraceView 寻找卡住主线程的地方,Systrace 获取 app 运行是线程的信息以及 API 的执行状况,避免在主线程执行 IO 操做。
问题:不合理的布局虽然能够完成功能,但随着控件数量越多、布局嵌套层次越深,展开布局花费的时间几乎是线性增加,性能也就越差;
定位及解决: 避免OverDraw致使的性能损耗;能够参考《Android性能优化(二)之布局优化面面观》
问题:同一时间动画执行的次数过多,致使CPU或GPU负载太重;
问题:内存抖动、内存泄漏都会致使:GC的次数越多、消耗在GC上的时间越长,CPU花在界面绘制上的时间相应越短;
解决:节省内存的分配空间,尽量的下降GC的频率,缩短GC的平均时间;CPU不被占用,卡顿的概率就会更低; 能够参考《Android性能优化(四)以内存优化实战》
问题:对线程开启方式的不一样选择以及不一样配置均可能致使卡顿的发生;
解决:任何耗时操做正确的移到异步里,类如I/O读写、数据库访问等都应该采用异步的方式,不能有“只是一个很小的文件”之类的想法,防微杜渐;
参考:
Android 性能模式 第一季
Android性能优化典范 - 第1季
Android性能优化之渲染篇
Android性能优化系列——Profile GPU Rendering
Profile GPU Rendering Walkthrough
Android 显示原理简介
Android 4.4 Graphic系统详解(2) VSYNC的生成
理解 VSync
了解Android 4.1,之三:黄油项目 —— 运做机理及新鲜玩意
Hierarchy Viewer使用详解
插播:金融壹帐通质量开放平台现提供测试一站式解决方案,包括UI自动化、测试过程管理、app评测、接口自动化、接口压测、舆情监控等测试服务,欢迎访问:http://fit-stg1.jryzt.com/Hyperion-server/html/index.html。