android性能优化(一)之UI渲染优化

阅读本文大概需要 2.6 分钟。

 

 

在众多高频面试题中,Android性能优化几乎可以说是必问的考题。

 

而此题一出,一场恶战已然拉开序幕,因为此话题牵扯面非常广,绝非三言两语就能够聊完。因此,非常有必要对性能优化做一下系统性的总结。

 

此篇作为性能优化系列开篇,是因为UI渲染优化最为初级,且杂项很多,面试时可以作为回答的起点。

 

对于UI渲染优化,记住一个宗旨:尽量减少layout布局嵌套层级和View个数。后面几项其实都是围绕着这个宗旨来做具体的优化的。

 

减少嵌套层级是减少View树的深度,原因一是如果View树过深,会导致findViewById()方法查找耗时,因为findViewById始终是从rootView开始深度优先遍历,二是会导致绘制重叠,从而造成过度绘制。

 

减少View个数,不言而喻能用最少的View个数实现UI效果,能够避免不必要的测量,放置,绘制的计算时间,和View的查找时间。

 

从代码的可读性和可维护性上来讲,最简洁的东西也是最好的。

 

一. 布局优化

 

1)尽量选用RelativeLayout,ConstrantLayout,LinearLayout,并发挥它们的特性使布局扁平化。同等扁平的情况下,选用LinearLayout性能更佳。

 

2)利用系统提供View的特殊属性,比如TextView的drawableStart属性等设置icon,就没有必要再增加一个ImageView。TextView滑动属性来避免嵌套一层ScrollView。

 

3)include一个layout时,可以考虑使用merge标签,如果布局的最外层和它所在的父容器控件相同,那么使用merge可以减少一个嵌套层级。

 

4)当某个布局的显现需要条件时,比如断网,获取数据失败等显示的UI,可以考虑使用ViewStub。其本质是一个宽高为0的View,非常轻量级,当需要显示时再inflate或者visible=true才会加载其布局。

 

5)使用LinearLayout自带的divider属性实现分割线,而不是在布局中手动添加一个额外的View作为分割线。

 

6)使用Space控件(而非View,LinearLayout等)进行合理的占位。其本质是一个onDraw实现为空的View,因此它只占位置,而不去渲染,使用它来进行占位填充比其它控件更加高效。

 

二. 自定义view优化

 

1)onDraw中避免对象的分配。原因是onDraw会执行多次,将导致频繁的GC,内存抖动,损耗性能。

2)使用Canvas的ClipRect方法避免过度绘制。

 

三.过度绘制

 

所谓过度绘制,就是GPU在同一区域进行了不必要的多次绘制。如果进行真机调试,打开“调试GPU过度绘制”,就能看到不同的区域有不同的颜色,表示过度绘制严重的程度。

 

过度绘制的原因就是嵌套层级过深,而且设置了多余的背景色和不可见区域的绘制所导致。

 

1)在合适的地方设置背景。

 

比如要对Activity设置背景颜色,无需在其根Layout上设置其background,因为系统本身就是把Activity的xml布局添加到一个叫“content”的FrameLayout中,Android系统本身已经对这个FrameLayout设置了背景颜色。

 

如果我们在layout.xml的根View中再设置背景颜色,就相当于GPU在同一区域绘制了2次背景色,导致过度绘制。

 

解决的办法有2种:

public class MyActivity extends AppCompatActivity {
    @Override  
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       //在setContentView之前
       findViewById(android.R.id.content).setBackgroundResource(xxx);
       setContentView(R.layout.xxx); //在自己的根layout中设置背景
}

 

public class MyActivity extends AppCompatActivity {
    @Override  
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       
       setContentView(R.layout.xxx); //在自己的根layout中设置背景
       getWindow().setBackgroundDrawable(null) //在setContentView之后
}

 

2)不可见区域不要绘制,ClipRect & QuickReject。

当我们在自定义View中用到ClipRect时,请只在ClipRect这个可见的矩形区域内进行绘制,而且通过QuickReject来判断另一个绘制的矩形区域是否和ClipRect所在矩形区域有相交,若无,则可避免区域外的绘制。

 

 

一切从android的handler说起(一)之message

一切从android的handler说起(二)之threadLocal

一切从android的handler说起(三)之UI线程不卡顿

一切从android的handler说起(四)之postDelay原理

一切从android的handler说起(五)之触摸事件模型

一切从android的handler说起(六)之生命周期来源

一切从android的handler说起(七)之Handler内存泄露


 

进入公众号,回复“程序员“可以领取一份计算机技术电子书福利合集

欢迎转发,关注公众号 肖晖

每天几分钟,掌握一个硬核面试知识点

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章