流畅度优化流程
- 通过SM对App的流畅度进行测试评估。
- 优化App的UI来提升流畅度。
- 通过lint静态扫描代码中的性能问题,进行优化。
- 深入分析和解决App逻辑层和IO层存在的问题。
测试评估App的流畅度
Android 4.1(JB)引入了VSync机制,VSync机制就像是一台转速固定的发动机(60转/s),有时候因为各种阻力某一圈工作量比较重,超过了16.6ms,那么这台发动机这秒内就不是60转了。
VSync机制可以通过其Loop来了解当前App最高绘制能力,其机制如下:
- Loop固定每隔16.6ms执行一次,最高刷新的帧率控制在60FPS以内。
- Loop在1s之内运行了多少次即可以表示当前App绘制的最高能力,也就是Android App卡顿的程度。
- 在一次Loop时如果执行时间超过了16.6ms,那么多于16.6ms的时间除以16.6ms,即是当前App的丢帧情况。
优化App的UI来提升流畅度
GPU过度绘制+Trace for OpenGL,解决UI过度绘制的问题。
Hierarchy Viewer,查找UI布局不合理的地方。
- 没有用的父布局。没有用的父布局是指没有背景绘制或者没有大小限制的父布局,这样的父布局不会对UI效果产生任何影响。我们把没有用的父布局通过<merge/>标签合并来减少UI的层次。
- 使用线性布局LinearLayout排版导致UI层次变深。如果有这类问题,我们就使用相对布局RelativeLayout代替LinearLayout,减少UI的层次。
- 不常用的UI被设置成了GONE,比如error页面,如果有这类问题,我们需要用<ViewStub/>标签代替GONE提高UI性能。
- <ViewStub/>标签:最大的优点是当你需要时才会加载,使用它并不会影响UI初始化时的性能。
Lint扫描,发现代码中的流畅度性能问题
- DrawAllocation:避免在绘制或者解析布局(draw/layout)时分配对象,比如在Ondraw()中实例化Paint对象。
- Wakelock:手机不能进入休眠状态,导致手机一直保持在高耗电状态。
- Recycle:某些资源,比如TypedArrays、VelocityTrackers,用完之后应该被回收,但是忘记回收。
- ObsoleteLayoutParam:Layout中无用的参数。
- UseCompoundDrawables:可优化的布局,包含一个Imageview和一个TextView的线性布局,可被采用CompoundDrawable的TextView代替。
- HandlerLeak:Handler的使用不当导致内存泄漏。
- UseSparseArrays:尽量用Android的SparseArray代替Hashmap。
- UseValueOf:需要常量对象时不应该直接new,应该用ValueOf转换。比如需要整数42的对象,不要直接用new Integer(42),应该用Intener.valueOf(42),这样可以省内存。
- DisableBaselineAlignment:如果LinearLayout被用于嵌套的layout空间计算,它的android:baselineAligned属性应该设置成false,以加速layout计算。
- InefficientWeight:当布线性局里只有一个控件,并且使用了weight属性,最好把width和heigth设为0,这样可以省略布局的measure过程。
- FloatMath:使用FloatMath代替Math。
- NestedWeights:避免嵌套weight,那将拖累执行效率。
- UnusedResources/UnusedIds:未被使用的资源会使程序变大,并且编译速度降低。
- Overdraw:如果为RootView指定一个背景Drawable,会先用Theme的背景绘制一遍,然后才用指定的背景,这就是所谓的“Overdraw”,可以设置theme的background为null来避免。
- UselessLeaf/UselessParent:View或view的父亲没有用,应该把他移除,避免影响加深布局的层次。
- UnusedNamespace:有些布局没必要使用namespace,会影响代码执行效率。
优化App的逻辑层
找出在主线程耗时较大的函数,看看是否能够通过优化逻辑去减少API的耗时,优化的方案大概是缓存某些数据在需要的时候能够更快地加载,或者把耗时的操作移出主线程,或者把滑动的过程中出现的耗时操作延时到滑动停止后才开始。
分析滑动的过程中CPU的工作,看看是否能让CPU优先执行主线程的工作,尽量不要被其他线程抢占。
Traceview,寻找卡住主线程的地方
有两种类型的函数可能会影响到流畅度:
- 主线程里占用CPU时间(Incl Cpu Time)很长的函数,特别要
- 留意在主线程的IO操作(文件IO、网络IO、数据库操作等)。
- 主线程调用次数(包括被调用和递归调用)很多的函数。
Systrace,获取App运行时线程的信息以及API的执行情况
Systrace有下面这些优点:
- 能直观地看到每个线程上面API的调用情况,包括API的耗时以及API的调用顺序。
- 能直观地看到每个线程的执行情况,包括各个线程的状态以及耗时,并且能够统计CPU里每个线程执行的耗时。
- 能够通过插入代码的方式,在Systrace里显示想要查看的API的耗时以及调用关系。