1.Android UI渲染机制
人眼所感觉的流畅画面,需要画面的帧数达到40帧每秒到60帧每秒,最佳的fps大概是在60fps左右,这是评价一个显卡性能高低的标准之一。
fps:Frame(画面、帧) per second。就是指动画或视频的画面数,每秒的帧数越多,就画面越流畅。当fps太低时,我们肉眼就能明显感觉到屏幕的闪烁,不连贯,图像显示效果和视觉效果很差。
在Android中,系统通过VSYNC信号触发对UI的渲染、重绘。其间隔时间是16ms。这个16ms就是1000ms(1s)中显示60帧画面的单位时间,即1000/60.(每16ms就会显示一帧)如果系统每次渲染的时间都保持在16ms之内,那么我们看见的UI界面将会非常流畅,这也就需要将所有程序的逻辑都保证在16ms内。如果不能再16ms内完成绘制,那么则会出现丢帧的现象,即当前该重绘的的帧被未完成的逻辑阻塞,例如,一次绘制任务耗时20ms,那么在16ms系统发出VSYNC信号时就无法绘制,该帧就会被丢弃,等待下次信号才开始绘制,导致16ms*2内部显示的是同一帧的画面,这就是画面卡顿的原因。
GPU:图形处理器,是显卡的“心脏”,主要负责显示图形。
2.避免Overdraw
Overdraw,过度绘制会浪费很多的CPU,GPU资源。
例如,系统默认绘制Activity的背景,而如果再给布局绘制重叠的背景,那么默认Activity的背景就属于无效的过度绘制——Overdraw。
通过开发者选项中——显示GPU过度绘制,可以通过界面的颜色查看Overdraw的绘制次数,从而尽量优化绘图的层次,尽量增大蓝色的区域,减少红色的区域。
3.优化布局层级
在Android中,系统对View进行测量、布局和绘制时,都是通过view树的遍历来进行操作的。如果一个view树的高度太高,就会严重影响测量、布局和绘制的速度因此优化布局的第一个方法就是降低View树的高度,官方API建议View树的高度不宜超过10层。
在早期的Android版本中,使用LinearLayout作为默认的根布局,现在已经使用RelativeLayout来代替LinearLayout作为默认的根布局,就是通过扁平的RelativeLayout来降低通过LinearLayout嵌套所产生的布局树的高度,从而提高UI渲染的效率
4.避免嵌套过多无用的布局
嵌套的布局只会让view树的高度越来越高,因此在布局时,需要根据自身不同的特点来选择不同的Layout组件,避免嵌套的发生
1>使用<include>标签重用Layout
在一个应用程序中,为了风格上的统一,很多界面都会存在一些共同的UI,比如一个应用的Toolbar、Bottombar等。对于这些共同的UI,如果在每个界面都来复制一段这样的代码,不仅不利于后期代码的维护,更增加了程序的冗余度。因此可以用<include>标签来定义一个这样的UI.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp"
android:textSize="30sp"
android:gravity="center"
android:text="this is a common ui">
</TextView>
注意:将android:layout_height和android:layout_width设置为0dp,这样就迫使开发者在使用时对宽和高进行赋值,否则无法看见
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:layout_height="match_parent"
android:layout_width="match_parent">
<TextView
android:text="HelloWorld"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<include layout="@layout/common_ui"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</RelativeLayout>
注意:如果你需要在<include>标签中覆盖类似原布局中的android:layout_XXX的属性,就必须在<include>标签中同时指定<pre name="code" class="html">android:layout_height和android:layout_width属性
2>使用<ViewStub>实现对View的延迟加载(只有在要显示的时候才会被渲染)
除了把一个view作为共通UI,并通过<include>标签来进行应用之外,还可以使用<ViewStub>标签实现对一个View的引用并实现迟加载。<ViewStub>是一个非常轻量级的组件,它不仅不可视,并且大小为0.
not_often_use.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="not often use layout"
android:textSize="30sp"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:layout_height="match_parent"
android:layout_width="match_parent">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Visible"
android:onClick="btnVisible"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Inflate"
android:onClick="btnInflate"
android:id="@+id/button2"
android:layout_alignBottom="@+id/button1"
android:layout_toEndOf="@+id/button1"/>
<ViewStub
android:id="@+id/not_often_use"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/not_often_use"/>
</RelativeLayout>
通过引用将not_often_use放在ViewStub中
在运行程序以后,<ViewStub>中引用的布局并没有显示出来,那么怎么才能加载出布局呢??
先找到<ViewStub>组件
mViewStub = (ViewStub)findViewById(R.id.not_often_use);
有两种方法:
*VISIBLE
通过调用ViewStub的setVisiblility()方法来显示这个view,代码如下:
mViewStub.setVisibility(View.VISIBLE);*inflate
通过调用inflate()方法显示这个view,代码如下:
View inflateView = mViewStub.inflate();
这个方法可以返回引用的布局。通过findViewById找到布局里面的空间,并可以进行设置
View inflateView = mViewStub.inflate();
TextView textView = (TextView)inflateView.findViewById(R.id.tv);
textView.setText("HAHA");
不管使用哪种方法,一旦ViewStub被设置为可见了或是被inflate了,ViewStub就不在了,取而代之的就是被inflate的Layout.
总结:ViewStub只会在显示时,才会渲染整个布局。
那么View.GONE和ViewStub有什么区别呢??
答:它们的共同点就是初始化的时候都不显示,但是ViewStub只有在显示的时候才会渲染布局,而View.GONE,在初始化布局树的时候就已经添加在布局树中了,相比之下,ViewStub的效率更高
5.Hierarchy Viewer
Hierarchy Viewer无法在真机上进行使用它只能在工厂的demo机和模拟器上使用。
通过这个工具,就会很快的找到view树中的冗余布局,从而有效的优化布局。