Android 性能优化之布局
1.避免过度OverDraw
Overdraw就是过度绘制,是指在一帧的时间内(16.67ms)像素被绘制了多次,理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这个操作耗时超过16.67ms时,就会出现掉帧现象,也就是我们所说的卡顿,所以对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。
ndroid提供了测量Overdraw的选项,在开发者选项-调试GPU过度绘制(Show GPU Overdraw),打开选项就可以看到当前页面Overdraw的状态,就可以观察屏幕的绘制状态。该工具会使用三种不同的颜色绘制屏幕,来指示overdraw发生在哪里以及程度如何,其中:
没有颜色: 意味着没有overdraw。像素只画了一次。
蓝色: 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。
绿色: 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。
浅红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。
暗红: 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的,要修复它们。
优化布局层级
- 在Android中,系统会对View 进行测量、布局、和绘制时,都是对View数的遍历来进行操作的。如果view树的高度太高,就会严重影响测量、布局和绘制的速度。因此优化布局的第一个方法就是降低View树的高度.并且Google也在其API中建议View树的高度不宜超过10层。
- Android提供的Layout控件主要包括LinearLayout、TableLayout、FrameLayout、RelativeLayout。每个控件都有优势,尺有所短寸有所长,对于LinearLayout而言只能控制竖直方向或者横向的布局,而RelativeLayout几乎可以用于描述任意复杂度的界面。所以在表达复杂页面的时候建议用RelativeLayout,其原因是通过扁平的RelativeLayout
来降低LinearLayout嵌套所产生布局树的高度,从而提高UI渲染的效率。
-
避免嵌套过多无用的布局
例如:布局文件中只有一个Button 但是它的父布局有三个LinearLayout。其中俩个LinearLayout都是可以去掉的。- 使用 include 标签重用Layout
- 在开发的时候很多界面风格是都是统一的,这样就产生了一些共通的UI。比如Toolbar、Bottombar等。对于这些共通的UI,如果在每个界面中都来复制一段这样的代码,不仅不利于后期的维护,更增加了程序的冗余度。可以使用include标签来定义一个共通的UI。
- 使用 include 标签重用Layout
<android.support.v7.widget.Toolbar
android:layout_width="0dp"
android:layout_height="0dp"
android:textSize="30sp"
android:gravity="center"
/>
继续
- 代码中只有一个TextView,并且居中显示了一段文字,但更多的时候我们需要通过相应的Layout组件将若干的控件组成一个共通的UI,在这里暂且把它作为一个共通UI。在代码中宽度和高度都设置为0dp,这样就迫使开发者在使用时对高度宽度进行赋值,否则将无法看见这个界面。
- 那么怎么用include应用呢
<include layout="@layout/common_on"
android:layout_width="match_parent"
android:layout_height="match_parent/>
3.使用 ViewSub实现View 的延迟加载。
除了把一个View作为共通UI,并通过include标签来进行应用之外,还可以使用ViewSub标签来实现对一个view引用并实现延迟加载。ViewSub是一个非常轻量级的组件,它不仅不可视,而且大小为0。
使用ViewSub来引用TextView
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/tv"
android:textSize="30sp"
android:gravity="center"
/>
<ViewSub
android:id="@+id/not_often_use"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/not_often_use"
android:text="没出来"
/>
运行后发现没有显示出来,那么如何让它显示出来呢?
- 首先通过普通的findViewById()方法找到ViewSub组件,这点与一般的组件基本相同,代码如下所示.
mViewSub=(ViewSub) findViewById(R.id.not_often_use);
接下来,有俩个方法来重新显示这个View
VISIBLE
- 通过点用ViewSbu的setVisibility()来显示这个View,代码如下所示
mViewStub.setVisibility(View.VISIBLE);
inflate
- 通过调用ViewSub的inflate()方法来显示这个view,如代码所示
-View inflateView=mViewSub.inflate();
这俩种方式都可以让ViewSub重新展开,显示引用的布局,而唯一不同的是inflate()方法可以返回引用的布局,从而可以在通过View.findViewById()方法来找到对应的控件,代码如下:
View inflateView=mViewSub.inflate();
TextView textView=(TextView)infateView.findViewById(R.id.tv);
textView.setText("Haha!");
- 而不管使用那种方式,一旦ViewSub被设置为可见或是被inflate了,ViewSub就不存在了,取而代之的是被inflate的Layout,并将这个Layout的id重新设置为ViewSub中通过android:inflatedId属性所指定的ID,这也是为什么俩次调用inflate方法汇报错的原因。
看到这里也许你会问这个ViewSub标签与View.GONE这个方式来隐藏一个View有什么区别呢?的确,它们的共同点都是初始化的时候不显示,但是ViewSub标签智慧在显示时,才去渲染整个布局,而View.GONE,在初始化布局树的时候就已经天假在布局树上了,相比之下ViewSub更有效率。
Hierarchy Viewer
布局优化器,Hierarchy Viewer无法在真机上进行使用,它只能在工厂的demo机和模拟器上使用,即非加密过的设备。Google的“大神”——Romain Guy提供了一个开源项目View Server,通过这个程序可以让普通的手机也能使用Hierarchy Viewer,有兴趣的朋友可以去了解一下。
下面在模拟器上使用这个工具,它位于/sdk/tools 目录下。在命令行输入hierarchyviewer.bat启动程序。
启动虚拟机之后,运行hierarchyviewer.bat 如图
view 的树形结构图就出来了。上面有测量、布局、draw所需要的时间,可以看出那个view渲染出来慢,还有就是它用绿色、红色、黄色代表渲染速度。红色最慢,黄色次之,绿色最好。