不断学习,做更好的自己!💪

视频号



欢迎打开微信,关注我的视频号:程序员朵朵

​​

前言

什么是性能
【Android -- 性能优化】布局优化_嵌套

快(流畅的体验)
稳(稳定)
省(省电/流量)
小(安装包小)
这四点很形象的代表了性能的四个方面,同时也让我们知道我们 App 现在是否是款性能良好的 APP,如果有一项不达标,那么说明我们的应用有待优化。

过度绘制

定义

过度绘制(Overdraw):指的是屏幕上的某个像素在同一帧的时间内被绘制了多次。

检测

Android手机上面的开发者选项提供了工具来检测过度绘制,可以按如下步骤来打开:
​​​开发者选项->调试GPU过度绘制->显示过度绘制区域​​​【Android -- 性能优化】布局优化_嵌套_02

注意:
有些过度绘制是无法避免的。因此在优化界面时,应该尽量让大部分的界面显示为真彩色(即无过度绘制)或者为蓝色(仅有 1 次过度绘制)。尽量避免出现粉色或者红色。

优化

1. 移除布局中不需要的背景

  • 移除 Window 默认的 Background
    方式一:在theme中设置
<style name="AppTheme" parent="主题">
<item name="android:windowBackground">@null</item>
</style>

方式二:在Activity的onCreate()方法中添加:

getWindow().setBackgroundDrawable(null);
  • 移除控件中不需要的背景

2. 将layout层级扁平化

  • Layout Inspector来查看layout的层次结构。
    在Android Studio中点击 Tools > Android > Layout Inspector。然后在出现的 Choose Process 对话框中,选择想要检查的应用进程即可。
    Layout Inspector会自动捕获快照,然后会显示以下内容:
  • View Tree:视图在布局中的层次结构。
  • Screenshot:每个视图可视边界的设备屏幕截图。
  • Properties Table:选定视图的布局属性。
  • 使用嵌套少的布局
    我们可以使用 LinearLayout 和 RelativeLayout 来完成。但是 LinearLayout 相比于 RelativeLayout,就多了一层,所以 RelativeLayout 明显是一个更优的选择。
  • 使用 include 标签能够复用布局
    可以将一个指定的布局文件加载到当前的布局文件中。如下:
<?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/title_bar"/>

<TextView
android:id="@+id/first_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rect_color_primary"
android:gravity="center"
android:padding="@dimen/item_spacing"
android:text="FirstExample"
android:textColor="@color/colorPrimaryDark"/>

...
</LinearLayout>

需要注意的是,如果include标签指定了layout_*这种属性,那么要求
android:layout_width和android:layout_height必须存在,否则其他的android:layout_*形式的属性无法生效。

- 使用 merge 标签减少嵌套
一般和include标签一起使用从而减少布局的层级。在上述示例中,由于当前布局是一个竖直方向的LinearLayout,用过merge标签就可以去掉多余的那一层LinearLayout。如下:
<merge xmlns:android="http://schemas.android.com/apk/res/android">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button2"/>
</merge>

ViewStub
ViewStub继承了View,它非常轻量级且宽和高都是0,因此它本身不参与任何的布局和绘制过程。比如:网络异常时的界面,这个时候就没有必要在整个界面初始化将其加进来,通过ViewStub就可以做到在使用的时候再加载,提高了程序初始化的性能:

<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/panel_import"
android:layout="@layout/layout_empty_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"/>

panel_import是@layout/layout_empty_txt这个布局的根元素的id。
在需要加载ViewStub中的布局时,可以按照如下两种方式:

(ViewStub)findViewById(R.id.stub_import).setVisibility(View.VISIBLE);

或者

View mImportPanel = ((ViewStub)findViewById(R.id.stub_import)).inflate();

通过上述两个方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub就不再是整个布局结构中的一部分了。

  • 使用lint来优化布局的层次结构
    lint是一个静态代码分析工具,可以用来协助优化布局的性能。要使用lint,点击Analyze> Inspect Code即可。

布局性能方面的信息位于Android> Lint> Performance下,我们可以点开它来看下一些优化建议。
下面是lint的一些优化技巧:

  • 使用复合图片
    如果一个线性布局中包含一个 ImageView 和一个 TextView,可以使用复合图片来替换掉
  • 合并根节点
    如果一个FrameLayout 是整个布局的根节点,并且也没有提供背景、留白等等,那么可以使用标签来替换掉,因为DecorView本身就是一个FrameLayout。
  • 移除布局中无用的叶子
    布局是一个树形的结构,如果一个布局没有子 View 或者背景,那么可以把它移除掉(这布局本身就不可见了)。
  • 移除无用的父布局
    如果一个布局没有兄弟,也不是ScrollView 或者根 View,并且也没有背景,那么可以把这个父布局移除掉,然后把它的子view移到它的父布局下。
  • 避免过深的层次结构
    过多的布局嵌套不利于性能,可以使用更扁平化的布局,如RelativeLayout、GridLayout、ConstraintLayout等布局来提高性能。布局默认的最大深度为10。

布局优化技巧

  1. 如果父控件有颜色,也是自己需要的颜色,那么就不必在子控件加背景颜色
  2. 如果每个自控件的颜色不太一样,而且可以完全覆盖父控件,那么就不需要再父控件上加背景颜色
  3. 尽量减少不必要的嵌套
  4. 能用​​LinearLayout​​​和​​FrameLayout​​​,就不要用​​RelativeLayout​​​,因为​​RelativeLayout​​控件相对比较复杂,测绘也想要耗时。
  5. 使用​​include​​​和​​merge​​增加复用,减少层级
  6. ​ViewStub​​ 按需加载,更加轻便
  7. 复杂界面可选择​​ConstraintLayout​​,可有效减少层级