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

主目录见: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一起联合使用。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容