主要影响Android应用中页面显示的速度。1个页面经过递归 完成测量 & 绘制过程 = measure、layout 过程,而这个过程过长则会给用户带来卡顿的视觉效果。android
布局优化的思路其实很简单,就是尽可能减小布局文件的层级。布局的层级少了,这就意味着Android绘制时工做量少了,那么程序的性能天然就高了。性能优化
若是布局中便可使用LinearLayout也可使用RelativeLayout,那就采用LinearLayout。由于RelativeLayout在绘制时须要对子View分别进行了竖直和水平方向的两次测量,而Linearlayout在绘制时是根据咱们设置的方向分别调用不一样的测量方法。注意一点若是LinearLayout中子View使用了layout_weight属性时一样须要对子View进行两次测量以肯定最终大小(对此不了解的小伙伴们能够查看源码中onMeasure和onLayout方法本文就很少贴源码)。LinearLayout和FrameLayout都是一种性能耗费低的布局。可是不少时候单纯经过一个LinearLayout或FrameLayout没法实现产品的效果,须要经过嵌套的方式来完成。这种状况下建议使用RelativeLayout,由于嵌套就至关于增长了布局的层级,一样会下降程序的性能。
bash
评论屡次提到Constraintlayout,因为笔者用的较少忘了说,疏忽了,疏忽了[手动哭笑]。面对复杂度高的布局(比RelativeLayout和LinearLayout屡次嵌套)Constraintlayout确实更简单,绘制时间更短。但面对复杂度较低的布局,RelativeLayout比ConstraintLayout在onMesaure阶段快数倍。下图为Hierarchy Viewer的测试结果(里面一个TextView,一个ImageView,关于Hierarchy Viewer的使用会在下文布局调优工具中):服务器
使用 <include>标签提取布局间的公共部分,经过提升布局的复用性从而减小测量 & 绘制时间网络
<!--抽取出的公共布局:include_title.xml-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:paddingLeft="15dp"
android:src="@mipmap/ic_titilebar_back"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/title"
android:textColor="@color/white"
android:textSize="18sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="center"
android:padding="15dp"
android:text="@string/more"
android:textColor="@color/white"
android:textSize="16sp"/>
</RelativeLayout>
<!--布局:activity_main.xl引用公共布局include_title.xml-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/include_title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Hello World!" />
</LinearLayout>
复制代码
<include> 标签只支持以android:layout_开头的属性(android:id除外),须要注意一点若是使用了android:layout_*这种属性,那么要求android:layout_width 和android:layout_height必须存在,不然其余android:layout_*形式的属性没法生效,下面是一个指定了android:layout_*属性的示例app
<include
android:id="@+id/include_title"
android:layout_width="match_parent"
android:layout_height="48dp"
layout="@layout/include_title"/>
复制代码
<merge> 布局标签通常和 <include> 标签一块儿使用从而减小布局的层级。例如当前布局是一个竖直方向的LinearLayout,这个时候若是被包含的布局也采用了竖直方向的LinearLayout,那么显然被包含的布局文件中的LinearLayout是多余的,这时经过 <merge> 布局标签就能够去掉多余的那一层LinearLayout。以下所示:ide
<!--抽取出的公共布局:include_title.xml-->
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</merge>
<!--布局:activity_main.xl引用公共布局include_title.xml-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/include_title"/>
</LinearLayout>
复制代码
ViewStub继承了View,它很是轻量级且宽和高都为0,所以它自己不参与任何的绘制过程,避免资源的浪费,减小渲染时间,在须要的时候才加载View。所以ViewStub的意义在于按需求加载所需的布局,在实际开发中,不少布局在正常状况下不会显示,好比加载数据暂无数据,网络异常等界面,这个时候就不必在整个界面初始化的时候将其加载进来,经过ViewStub就能够作到在使用时在加载,提升了程序初始化时的性能。以下一个ViewStub的示例:工具
<!--暂无数据页:empty_data.xml-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/ic_empty_order"/>
<TextView
android:layout_below="@+id/iv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂无数据"/>
</LinearLayout>
<!--布局activity_main.xml-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
//view_stub是ViewStub的id,empty是empty_data.xml这个布局根元素的id
<ViewStub
android:id="@+id/view_stub"
android:inflatedId="@+id/empty"
android:layout="@layout/empty_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<!--MainActivity-->
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//加载ViewStub中的布局的两种方式setVisibility或inflate
mViewStub.setVisibility(View.VISIBLE);
mViewStub.inflate();
}
复制代码
使用ViewStub需注意:当ViewStub经过setVisibility或inflate方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub就再也不是布局结构中的的一部分。目前ViewStub中的layout还不支持使用<merge> 标签。布局
布局属性 wrap_content 会增长布局测量时计算成本,应尽量少用post
在绘制布局中,某些状况下咱们能够省去部分控件的使用。下文介绍几种常见的状况:
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock" // 设置左边显示的icon
android:drawablePadding="10dp" // 设置icon和文本的间距
android:drawableRight="@mipmap/icon_right" // 设置右边显示的icon
android:text="@string/account_unlock"
/>
复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/divider_line"
android:dividerPadding="16dp"
android:showDividers="middle"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock"
android:drawablePadding="10dp"
android:drawableRight="@mipmap/icon_right"
android:background="@color/color_FFFFFF"
android:text="@string/account_unlock"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock"
android:drawablePadding="10dp"
android:drawableRight="@mipmap/icon_right"
android:background="@color/color_FFFFFF"
android:text="@string/account_unlock"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock"
android:drawablePadding="10dp"
android:drawableRight="@mipmap/icon_right"
android:background="@color/color_FFFFFF"
android:text="@string/account_unlock"
/>
<!--Shape:divider_line.xml-->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorPrimary"/>
<size android:height="1dp"/>
</shape>
</LinearLayout>
复制代码
核心代码就是对LinearLayout设置divider
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="10dp"
android:text="@string/test_text"
android:textSize="20sp"
android:textColor="@color/colorPrimary"/>
复制代码
经过lineSpacingExtra设置行间隔,test_text内容为:
<string name="test_text">标题:%1$s\n时间:%2$s\n内容:%3$s</string>
复制代码
在MainActivity中的使用
mText.setText(String.format(getResources().getString(R.string.test_text), "测试", "2018-8-9","测试测试"));
复制代码
占位符的使用方法:%n表示第n位要被替换的,$s表示字符串类型占位符,$d表示整型占位符,$f表示浮点型占位符
在实际开发中哪怕注意了上述的优化方案,不免仍是会出现布局性能的问题。这时咱们可以使用布局调优工具来分析问题。本文将介绍经常使用的几种工具。
Lint 是Android Studio 提供的 代码扫描分析工具,它能够帮助咱们发现代码结构/质量问题,同时提供一些解决方案,并且这个过程不须要咱们手写测试用例。 Lint 的使用路径: 工具栏 -> Analyze -> Inspect Code
默认是检查整个项目,咱们能够点击 Custom scope 自定义检查范围
固然你也能够选择特定的类进行检查点击下图红色箭头标识的地方
点击“+”号新增一个检查范围:
咱们左击想扫描的文件,点击右边对应的按钮。能够看到文件边色了,红框显示需扫描24个文件,点击OK→OK
稍等一下子,会弹出 Inspection 对话框,显示检查结果。
Lint 的警告严重程度有如下几种:
本文对Lint的介绍就到此若是对Lint想了解更多的小伙伴能够点击Lint
Hierarchy Viewer 是Android Studio 提供的UI性能检测工具。可得到UI布局设计结构 & 各类属性信息,帮助咱们优化布局设计 。
使用Hierarchy Viewer ,您的设备必须运行Android 4.1或更高版本。若是您是使用真机的话需注意如下两点:
Hierarchy Viewer 的使用路径: 工具栏 ->Tools->Android->Android Device Monitor(默认是显示DDMS窗口,更改可点击Open Perspective->Hierarchy Viewer,也能够直接点击 Hierarchy Viewer Button),以下图所示:
更改后显示Hierarchy Viewer的窗口以下图所示:
若是您看的的视图排列不同,可选择 Window->Reset Perspective 返回默认布局。下面介绍下基本窗口:
上图图标的使用左到右介绍:
这些点大体对应于处理管道的度量,布局和绘制阶段。点的颜色表示该节点相对于本地系列中全部其余配置节点的相对性能。
所测量的是每一个节点相对于兄弟视图的性能,所以配置文件中始终存在红色节点,除非全部视图执行相同,而且它并不必定意味着红色节点执行得不好(仅限于它是最慢的视图)在本地视图组中)。
本文对Hierarchy Viewer的介绍就到此了,想了解更多的小伙伴能够点击Hierarchy Viewer
开发者选项是Android 系统为开发者提供的一个APP验证、调试、优化等各类功能的入口(需Android 4.1以上)。本文主要对GPU过分绘制菜单介绍,对其余菜单功能有兴趣的小伙伴能够参考探索Android手机开发者选项
GPU过分绘制是指在屏幕上某个像素在同一帧的时间内被绘制屡次,产生的主要缘由有两个:
不一样手机开发商都对手机界面作了定制化处理,打开手机开发者选项的方式各不相同,但基本的功能菜单都是相似的。因为笔者如今用的是小米5s Plus,所以打开方式是设置->个人设备->所有参数->连续点击MIUI版本,提示开发者模式开启便可(不一样MIUI版本开启路径也不同)。而后设置->更多设置,高级设置或者其余设置等等菜单中便可看到开发者选项的菜单了。
在开发者选项中点击调试GPU过分绘制,以下图所示
这时屏幕会出现各类颜色,每种颜色的定义为:
以上就是本文对布局调优工具的介绍,固然还有其余布局调优工具如Systrace本文就不一一介绍了。
本文主要讲解Android 性能优化中的 布局优化。如对小伙伴们有帮助,麻烦给个喜欢,不足之处请留言私聊点出谢谢!
对Flutter有兴趣的可参考Flutter学习之路(一)Flutter简介及Window下开发环境搭建