Android性能优化之布局优化

布局的优化其实说白了就是减少层级,越简单越好,减少overdraw,就能更好的突出性能。例如删除布局中无用的控件和层级;选择性能较低的ViewGroup比如ConstraintLayout;如果布局中即可以使用LinearLayout也可以用RelativeLayout,那就采用LinearLayout。因为RelativeLayout的功能负载,它的布局过程需要花费更多的CPU的时间。

Android布局优化常用方法

1.include标签
include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,这在布局编写方便提供了大大的便利。例如下拉刷新和上拉加载的header和footer.

<com.aspsine.swipetoloadlayout.SwipeToLoadLayout
    android:id="@+id/swipeToLoadLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include
        android:id="@id/swipe_refresh_header"
        layout="@layout/layout_twitter_header" />

    <android.support.v7.widget.RecyclerView
        android:id="@id/swipe_target"
        android:background="@color/circle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include
        android:id="@id/swipe_load_more_footer"
        layout="@layout/layout_classic_footer" />
</com.aspsine.swipetoloadlayout.SwipeToLoadLayout>

在代码中直接用layout中的控件id直接获取对应的控件。
注意:
1.findViewById 出现 NullPointerException 的情况
如果 include 一个布局时,并没有给 标签设置 id 属性,那么你直接使用 findViewById 来找 include 指定布局中控件是没有问题的。
但是,一旦你为 标签设置了 id ,就不能直接把它里面的控件当成主布局文件中的控件来直接获取了,必须先获得这个 标签指定的布局文件,再通过该布局文件 findViewById 来获得其子控件。

2.如果想在 标签中覆盖被包含布局所指定的任何 android:layout_* 属性,必须在 标签中同时指定 android:layout_width 和 android:layout_height 这两个属性。

2.viewstub标签
viewStub它是非常轻量级且宽高都为0,因此它本身不参与任何的布局和绘制过程。viewStub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。通过viewStub可以做到在使用时再加载(延时加载),提高了程序初始化的性能。例如网络异常的界面;信息出错的界面。

<?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">
<com.aspsine.swipetoloadlayout.SwipeToLoadLayout
    android:id="@+id/swipeToLoadLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include
        android:id="@id/swipe_refresh_header"
        layout="@layout/layout_twitter_header" />

    <android.support.v7.widget.RecyclerView
        android:id="@id/swipe_target"
        android:background="@color/circle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include
        android:id="@id/swipe_load_more_footer"
        layout="@layout/layout_classic_footer" />
</com.aspsine.swipetoloadlayout.SwipeToLoadLayout>

    <!--网络无效时展示的界面-->
    <ViewStub
        android:id="@+id/network_error_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/net_work_error"></ViewStub>
</RelativeLayout>

其中net_work_error.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">
        <ImageView
            android:layout_above="@+id/tv_error"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/pic_network"/>
        <TextView
            android:id="@+id/tv_error"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="糟糕~网络罢工啦!"
            android:layout_alignParentBottom="true"
            android:textSize="@dimen/dp_14"/>

    </RelativeLayout>

代码中初始化



public class MainActivity extends Activity {

    private ViewStub viewStub;
    private boolean flag = true;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_other);
       //只执行一次
      if(!mIsFolderViewInit) {
            viewStub = (ViewStub) findViewById(R.id.floder_stub);
        viewStub.inflate();
    }

    }

    public void handClick(View view) {
        if (flag) {
            viewStub.setVisibility(View.GONE);
        } else {
            viewStub.setVisibility(View.VISIBLE);
        }
        flag = !flag;
    }
}

ViewStub在XML中定义的id只在一开始有效,一旦ViewStub中指定的布局加载之后,这个id也就无效了。当ViewStub一旦初始化,它会被内部的布局替换掉。ViewStu不支持merger标签。

3.merge标签
merge标签和include标签配合使用从而减少布局的层级
merge标签可用于两种典型情况:
a. 布局顶结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容试图的parent view就是个FrameLayout,所以可以用merge消除只剩一个。
b. 某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。

 <!--网络无效时展示的界面-->
 <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_above="@+id/tv_error"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/pic_network"/>
        <TextView
            android:id="@+id/tv_error"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="糟糕~网络罢工啦!"
            android:layout_alignParentBottom="true"
            android:textSize="@dimen/dp_14"/>

    </merge >

看一下TreeView结构图吧
没有使用merge 标签的

这里写图片描述

使用merge标签之后的
这里写图片描述

你会发现少了一层嵌套,减少不必要的RelativeLayout。

4.Android最新的布局方式ConstaintLayout 
ConstraintLayout允许你在不适用任何嵌套的情况下创建大型而又复杂的布局。
它与RelativeLayout非常相似,所有的view都依赖于兄弟控件和父控件的相对关系。但是,ConstraintLayout比RelativeLayout更加灵活,目前在AndroidStudio中使用也十分方便,就和以前的拖拉控件十分相似。
ConstraintLayout是Android Studio 2.2中主要的新增功能之一。
如果没有可以build.gradle中添加:
implementation ‘com.android.support.constraint:constraint-layout:1.0.2’

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.UIActivity">
    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:src="@mipmap/ic_launcher"
        android:layout_marginStart="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/iv_image"
        android:text="loser的自述"
        android:textSize="16sp"
        app:layout_constraintLeft_toRightOf="@+id/iv_image"
        android:layout_marginStart="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_title"
        android:layout_toRightOf="@+id/iv_image"
        android:text="生活中没有失败者,只有那些在成功前就放弃的人"
        android:textSize="12sp"
        app:layout_constraintTop_toBottomOf="@+id/tv_title"
        android:layout_marginTop="16dp"
        app:layout_constraintLeft_toLeftOf="@+id/tv_title" />
</android.support.constraint.ConstraintLayout>

看效果图吧
这里写图片描述

其他的一些技巧:
1. 首次不需要使用的节点设置为GONE或使用viewstub。
2. 可通过设置->开发者选项->显示布局边界打开页面布局显示,看看是否有不必要的节点和嵌套。
3. 使用LinearLayout自带的分割线

布局调优工具

(1) hierarchy viewer
Hierarchy Viewer工具提供了一个可视化界面显示布局的层次结构,让我们可以进行调试,从而优化界面布局结构。
Hierarchy Viewer是随Android SDK发布的工具,位于Android SDK/tools/monitor.batt(Windows操作系统),通过此工具可以详细的理解当前界面的控件布局以及某个控件的属性(name、id、height等)。

这里写图片描述

选择一个节点,点击右上角的的这里写图片描述按钮,就可以获取到布局绘制的时间,如图:
这里写图片描述

View Hierarcy 同时能帮助你识别渲染性能比较低的部分。View节点中带有红色或黄色的点代表速度较慢的View对象。如单步运行应用程序那样,你可以这样来判断某个View 速度一直很慢,还是只在某个特定环境下速度才慢。
请注意,低性能并不表示一定有问题,特别像是ViewGroup对象,View的子节点越多,结构越复杂,性能越差。
View Hierarchy 窗口还可以帮助你找到性能问题。只要看每个View节点的性能指标(颜色点)就可以,你可以看到测量(布局或绘制)最慢的View对象是哪个,这样你就能快速确定,要优先察看哪个问题。

(2)Lint
Android Lint是Google提供给Android开发者的静态代码检查工具。使用Lint对Android工程代码进行扫描和检查,可以发现代码潜在的问题,提醒程序员及早修正。
这里写图片描述
AS的Lint配置:
这里写图片描述
一些Lint规则如下:
  1、使用组合控件: 包含了一个ImageView以及一个TextView控件的LinearLayout如果能够作为一个组合控件将会被更有效的处理。
  2、合并作为根节点的帧布局(Framelayout) :如果一个帧布局时布局文件中的根节点,而且它没有背景图片或者padding等,更有效的方式是使用merge标签替换该Framelayout标签 。
  3、无用的叶子节点:通常来说如果一个布局控件没有子视图或者背景图片,那么该布局控件时可以被移除(由于它处于 invisible状态)。
  4、无用的父节点 :如果一个父视图即有子视图,但没有兄弟视图节点,该视图不是ScrollView控件或者根节点,并且它没有背景图片,也是可以被移除的,移除之后,该父视图的所有子视图都直接迁移至之前父视图的布局层次。同样能够使解析布局以及布局层次更有效。
  5、过深的布局层次:内嵌过多的布局总是低效率地。考虑使用一些扁平的布局控件,例如 RelativeLayout、GridLayout ,来改善布局过程。默认最大的布局深度为10 。
  
(3)检查Overdraw
Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源。

在开发者工具中勾选Show GPU overdraw选项,观察UI上的Overdraw情况。
这里写图片描述

该工具会以不同的颜色绘制在屏幕上,标识出该块UI的Overdraw情况。
这里写图片描述

我们选择调试显示过度绘制区域,发现整个手机界面的颜色变了,在打开过度绘制选项后,其中的蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。

优化步骤如下:

  1. 删除window默认背景。
  2. 删除多层布局结构中重复设置的背景。
  3. 优化布局,减少布局层次。
  4. 检查组件,删繁去冗。
  5. 降低透明度。

(4)Profile GPU rendering  
Profile GPU Rendering工具对于相对于16毫秒每帧的基准花了多少时间来渲染UI,给了一个快速可视化的展示。

对于每个可视化的应用程序,它显示了一个图表,横坐标是时间轴,纵坐标是每帧渲染毫秒时间:
1. 当你使用你的应用时,在你的屏幕上从左到右显示竖直条,绘制帧性能随时间的变化;
2. 每个直条代表一帧的渲染,条的长短代表它渲染消耗的时间;
3. 这条绿线表示16毫秒每帧目标,任何时候一帧超过绿线,你的app将会丢掉一帧;

这里写图片描述

下表介绍了使用运行 Android 6.0 及更高版本的设备时分析器输出中某个竖条的每个区段。
这里写图片描述

此篇仅限于自己学习的笔记,有不足的地方,欢迎大家评论指出

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值