性能优化(1.2)-布局优化(扁平化,Merge的使用,View

作者: ZJ_Rocky | 来源:发表于2017-11-02 13:26 被阅读144次

    主目录见:Android高级进阶知识(这是总目录索引)
     今天之所以讲这一篇主要是为了下一篇[APP启动速度优化实例解析]做铺垫的,我们都知道我们解决UI卡顿问题中主要就是:

    1. 优化CPU的计算时间或者不必要的布局导致测量布局时间变长;
    2. 优化GPU的过度绘制,主要方法可以在手机打开GPU检测或者在Android studio中的Hierarchy Viewer可以查看层级。

    针对这两种情况一般会有下面的因素或者处理方式可以优化:

    • 在拼接字符串时候尽量使用StringBuilder避免大量的GC导致卡顿;
    • 避免在主线程做大量的计算任务,比如递归的操作导致CPU时间占用长导致卡顿;
    • 去掉window的背景,因为DecorView默认会有一个纯色的背景,在我们布局设置了背景的话,那么这个背景对我们来说是多余的;
    • 去掉不必要的背景,因为在我们布局有嵌套的情况下,如果都设置了背景的话有可能存在不必要的背景导致重绘;
    • 利用clipRect的方式来减少绘制层数,一个典型的例子就是扑克牌重叠导致重绘;
    • 利用include,merge,ViewStub等标签来减少嵌套层数。

    今天我们就是主要来讲的最后一种方法的使用,这个方式应该说能很有效解决过渡绘制的问题。

    一.目标

    我们今天的目标也是很简单的,就是看这几个标签是怎么使用的,然后能在实际应用中使用到,所以今天目标:
    1.学会include,merge,ViewStub这三个标签怎么使用;
    2.明白什么情况下使用哪个标签以及用这三个标签来优化。

    二.标签使用

    1.<include/>重用布局

    首先我们第一个来讲讲<include/>的使用,若几个布局界面存在较多的共同模块,可以进行代码块的重用,编写进入一个共同的布局里面,然后在多个布局文件中使用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="50dp">
        <Button
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:background="@mipmap/back_arrow"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            />
    
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="标题"
            android:textColor="@color/colorPrimary"
            android:textSize="20sp"
            android:gravity="center"/>
    
        <Button
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:background="@mipmap/btn_search"
            android:layout_gravity="center"/>
    </LinearLayout>
    

    这里只是简单布局了下,大家凑合着看哈,效果图如下:

    顶部栏

    那么如果我们的项目中会使用多次这个顶部栏的话(当然增加顶部栏不会每个页面include),那么我们这时候就可以用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"
        >
       <include layout="@layout/layout"/>
    </LinearLayout>
    

    这样我们就可以把公共的布局抽出来当做独立的部分了,这个标签应该来说用的还是比较多的。

    2.<merge/>减少布局层数

    我们知道我们布局解析的时候是一层一层递归调用rinflate,然后再回归讲view添加到父视图中去,最后整个视图才创建完毕。如果嵌套层数太多的话,就会导致这个解析的过程任务量变大从而导致解析速度变慢,这样的话我们可以用<merge/>标签来进行优化。我们首先来用androidstudio中的Hierarchy Viewer来查看我们刚才布局的层级:

    Hierarchy Viewer查看布局
    可以看到我们布局是从最上面的contentFrameLayout往下的,为什么呢?因为我们看setContentView源码的时候我们知道,我们的布局是在id为content的FrameLayout下面,如果不知道可以参考setContentView源码分析这篇文章,然后我们看到往下的话还有两层的LinearLayout布局,很明显,有一层LinearLayout是没有用的。所以我们来优化一下:
    <?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"
        >
       <include layout="@layout/layout"/>
    </merge>
    

    我们看到,这样的话就少了一层LinearLayout,也可以把里面的LinearLayout去掉,但是这样的布局就要又外层的布局来决定了:

    <?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="50dp">
        <Button
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:background="@mipmap/back_arrow"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            />
    
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="标题"
            android:textColor="@color/colorPrimary"
            android:textSize="20sp"
            android:gravity="center"/>
    
        <Button
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:background="@mipmap/btn_search"
            android:layout_gravity="center"/>
    </merge>
    

    那么我们外层的布局就要改成:

    <?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="50dp"
        android:orientation="horizontal"
        >
       <include layout="@layout/layout"/>
    </LinearLayout>
    

    好啦,到这里我们这个标签已经讲完了,这个标签的功能主要就是为了减少层数的。

    3.<ViewStub/>延迟加载布局

    其实ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才 会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:layout属性来设置。例如我们通过一个 ViewStub来惰性加载一个消息流的评论列表,因为一个帖子可能并没有评论,此时我可以不加载这个评论的ListView,只有当有评论时我才把它加 载出来,这样就去除了加载ListView带来的资源消耗以及延时:

    <?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"
        >
       <include layout="@layout/layout"/>
    
       <ViewStub
           android:inflatedId="@+id/network_error_id"
           android:layout="@layout/network_error"
           android:id="@+id/network_error"
           android:layout_width="match_parent"
           android:layout_height="match_parent"/>
    </merge>
    

    这里的inflatedId指的是layout/network_error的跟id,android:layout指的是布局network_error的布局。然后我们要让他显示出来:

        if (null == netWorkError){
                ViewStub netWorkErrorStub = (ViewStub)findViewById(R.id.network_error);
                netWorkError = netWorkErrorStub.inflate();
            }else{
                netWorkError.setVisibility(View.VISIBLE);
            }
    

    ViewStub标签和GONE都是会使一个视图不可见,但是设置为GONE的话在渲染的时候还是会被添加到视图树里面,而ViewStub只有在inflate之后才会被添加到视图树上面,所以减少了一次性渲染的压力。
    注意事项

    • 判断是否已经加载过, 如果通过setVisibility来加载,那么通过判断可见性即可;如果通过inflate()来加载是不可以通过判断可见性来处理的,所以需要判断加载的视图是否为空来判断。
    • findViewById的问题,注意ViewStub中是否设置了inflatedId,如果设置了则需要通过inflatedId来查找目标布局的根元素。
    • ViewStub不能与merge一起联合使用。

    总结:今天我们讲了这三个标签,应该说是开发过程中用的比较频繁的,希望大家如果又遇到布局优化问题能想到用这个来解决,当然,今天只是讲了一小部分的内容,优化的内容还是很多的,希望我们一起努力。

    相关文章

      网友评论

      • 请叫我章鱼哥:您好问一下,<merge/>标签优化布局的话,外层布局必须指定布局如LineLayout或RelayoutLayout,不能再使用<merge/>嵌套了吧?:stuck_out_tongue:
        请叫我章鱼哥:@ZJ_Rocky :+1: 谢谢解惑
        ZJ_Rocky:@请叫我章鱼哥 是呀,不然就乱了

      本文标题:性能优化(1.2)-布局优化(扁平化,Merge的使用,View

      本文链接:https://www.haomeiwen.com/subject/mbcopxtx.html