1、FPS
腾讯GT为了更准备的来评测App的流畅度,寻找了一种新的评测方法来替代FPS,把这种新的方法命名为流畅度(SM)
用FPS测试APP时遇到的问题:
1)为什么有时候FPS很低,但是我们却不觉得App卡顿?
2)App停止操作之后,FPS还是一直在变化,这样的情况是否会影响FPS的准确度?
系统获取FPS的原理是这样的:手机屏幕显示的内容是通过Android系统的SurfaceFLinger类,把当前系统里所有进程需要显示的信息合成一帧,然后提交到屏幕进行显示。FPS就是1s内SurfaceFLinger提交到屏幕的帧数
根据原理,就可以解答上面的两个问题:
1)有时候FPS很低,我们却感觉不到卡顿,是因为如果你的App在1s内只有30帧的显示需求,比如画一个动画只画了0.5秒就画完了,那么FPS最高也只有30帧/秒,但这并不代表它是卡顿的。而如果屏幕根本没有绘制需求,即屏幕显示的画面是静止的,那FPS就为0。
2)App停止操作后FPS还一直变化,是因为屏幕每一帧的合成都是针对手机里的所有进程,那么即使你的App停止了绘制,手机里其他进程可能还在绘制,比如通知栏的各种消息,这会导致FPS继续变化。从这里我们也能看出,在测试的时候,其他的进程对FPS也是有影响的
通过分析发现,用FPS来衡量APP的流畅度,并不一定准确,通过研究FPS的获取原理,我们对Android的绘制显示系统有了初步的了解,然后我们再对Android的绘制显示系统进行进一步的分析,终于找到了一种全新的衡量流畅度的方式,就是我们上面说的SM。
(此处需要深入的了解Android绘制机制和原理)
流畅度优化过程:
1、通过SM对APP的流畅度进行测试评估
2、然后从最简单的App UI层入手,优化App的UI来提升流畅度。
- GPU过度绘制+Trace for OpenGL,解决UI过度绘制的问题(过度绘制是指在屏幕一个像素上绘制多次,可以通过Tracer for OpenGL ES进行详细的观察)
- Hierarchy Viewer,查找UI布局不合理的地方,主要进行以下几种分析: 1、没有用的父布局。没有用的父布局是指没有背景绘制或者没有大小限制的父布局,我们把没有用的父布局通过<merge/ >标签合并来减少UI的层次。
2、使用线性布局LinearLayout排版导致UI层次变深。如果有这类问题,我们就使用相对布局RelativeLayout代替 LinearLayout,减少UI的层次
3、不常用的UI被设置成了GONE,比如error页面,如果有这类问题,我们需要用<ViewStub />标签代替GONE提高UI性能
3、接着通过lint静态扫描发现一部分代码中存在的性能(Performance)问题,然后进行优化。
4、最后再进一步深入的分析和解决App逻辑层和IO层存在的问题--使用工具Traceview以及Systrace。
- 找出在主线程耗时较大的函数,看看是否能够通过优化逻辑去减少API的耗时,优化的方案大概是缓存某些数据在需要的时候能够更快地加载,或者把耗时的操作移出主线程,或者把滑动的过程中出现的耗时操作延时到滑动停止后才开始
- 分析滑动的过程中CPU的工作,看看是否能让CPU优先执行主线程的工作,尽量不要被其他线程抢占。
4.1 Traceview,寻找卡住主线程的地方
Traceview可以通过图形界面的方式让我们了解要跟踪程序的性能,并且能具体好每个函数的耗时和调用次数,所以我们主要是关注各个函数对性能的影响。一般来说,主要有两种类型的函数可能会影响到流畅度:
1)主线程里占用CPU时间(Incl Cpu Time)很长的函数,特别要留意在主线程的IO操作(文件IO、网络IO、数据库操作等)。
首先按CPU占用时间(Incl Cpu Time)降序排序,然后一个个去观察,找出哪些耗时的API在主线程上执行,再去看看代码,是否能移到非主线程执行,如果涉及IO操作看看能否做缓存。
2)主线程调用次数(包括被调用和递归调用)很多的函数。
4.2 Systrace,获取App运行时线程的信息以及API的执行情况
通过在函数前后插入Trace.beginSection(“MainActivity.OnCreate”)和Trace.endSection(),就能看到该函数的耗时和函数的调用关系
Systrace的优点:
1)能直观地看到每个线程上面API的调用情况,包括API的耗时以及API的调用顺序。2)能直观地看到每个线程的执行情况,包括各个线程的状态以及耗时,并且能够统计CPU里每个线程执行的耗时。3)能够通过插入代码的方式,在Systrace里显示想要查看的API的耗时以及调用关系。
4.3优化APP的IO层
IO分为网络IO和磁盘读写IO。在主线程进行长时间或者频繁的IO操作,对流畅度会有非常大的影响。
4.3.1 我们通过代码注入(hook)的方式,hook文件打开关闭以及读写的API,采集磁盘IO的信息,IO信息包括读写的次数、读写的线程、读写数据的大小等。然后通过监控的结果来判断是否存在IO层的问题。
4.3.2 Android提供的StrictMode
总结:
1、布局
1.1 尽量多使用RelativeLayout和LinearLayout,不要使用绝对布局Absolute-Layout:a)在布局层次一样的情况下,建议使用 LinearLayout代替RelativeLayout,因为LinearLayout性能要稍高一点。b)在完成相对较复杂的布局时,建议使用 RelativeLayout,RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局。
1.2将可复用的组件抽取出来并通过include标签使用。
1.3 使用ViewStub标签来加载一些不常用的布局。
1.4 动态地inflation view性能要比SetVisiblity性能要好。当然用VIewStub是最好的选择。
1.5 使用merge标签减少布局的嵌套层次。
1.6 去掉多余的背景颜色,减少过度绘制:a)对于有多层背景颜色的Layout来说,留最上面一层的颜色即可,其他底层的颜色都可以去掉。b)对 于 使 用Selector当 背 景 的Layout(比 如ListView的Item,会 使 用Selector来标记点击,选择等不同的状态),可以将normal状态的color设置为“@android:color/transparent”,来解决对应的问题。
1.7 使用compound drawables:包含ImageView和TextView的LinearLayout可以使用compound drawable实现,这样更高效 (注:compound drawables是指包含图片的Textview)
1.8 内嵌使用包含layout_weight属性的LinearLayout会在绘制时花费昂贵的系统资源,因为每一个子组件都需要被测量两次。 在使用ListView与GridView的时候这个问题显得尤其重要,因为子组件会重复被创建,所以要尽量避免使用Layout_weight。
2、针对ListView的性能优化
2.1 复用convertView:在getItemView中,判断convertView是否为空,如果不为空,可复用。
2.2 异步加载图片,item中如果包含有image,那么最好异步加载
2.3 快速滑动时不显示图片:当快速滑动列表时(SCROLL_STATE_FLING), item中的图片或获取需要消耗资源的view,可以不显示出来;而处于其他两种状态(SCROLL_STATE_IDLE和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来。
2.4 item尽可能地减少使用的控件和布局的层次。同时要尽可能地复用控件,这样可以减少ListView的内存使用,减少滑动时gc次数。ListView的背景色与cacheColorHint设置相同颜色,可以提高滑动时的渲染性能。
2.5 getView优化:ListView中getView是性能是关键,这里要尽可能地优化。getView方法中不能做复杂的逻辑计算,特别是数据库和网络访问操作,否则会严重影响滑动时的性能。
3、解放UI主线程
3.1 不要阻塞UI线程:占用CPU较多的数据操作尽可能放在一个单独的线程中进行,通过handler等方式把执行的结果交于UI线程显 示。特别是针对的网络访问、数据库查询和复杂的算法。目前Android提供了AsyncTask,Hanlder、Message和Thread的组合。 对于多线程的处理,如果并发的线程很多,同时有频繁的创建和释放,可以通过concurrent类的线程池解决线程创建的效率瓶颈。
3.2 不要在UI线程之外操作UI。
ps: 还是要去更深入的了解一下Android的相关知识,好多名词都不知道什么意思 ==