Android性能优化(二)布局优化

布局优化的主要思想其实很简单,就是尽量减少布局文件的层级,布局层级少了Android绘制时的工作量就少了,程序运行时的性能自然就高了。
既然知道了主要思想那么接下来我们要做的工作就明朗了。我们可以从两方面着手进行优化:

第一、优化布局方案

在写布局的过程中我们应该避免无用的控件和层级,并且结合具体的使用场景去选择ViewGroup。避免无用的控件很简单,不必多说。那如何去选择性能更好的ViewGroup呢?这里的ViewGroup也就是我们常说的五大布局——FrameLayout、LinearLayout、RelativeLayout、AbsoluteLayout、TableLayout。其中FrameLayout、LinearLayout、RelativeLayout是最常用的,因此这里只对这三种布局进行分析。

FameLayout

FrameLayout是五大布局中最简单的一个布局,在这个布局中,整个界面被当成一块空白备用区域,所有的子元素都不能被指定放置的位置,它们统统放于这块区域的左上角,并且后面的子元素直接覆盖在前面的子元素之上,将前面的子元素部分和全部遮挡。相同层级布局中 FrameLayout的效率也是最高的 占用内存相对来说也是较小的。但是FrameLayout也有其缺陷:很难去组织多个子元素位置和关系。当然如果只有一个子元素,当然是使用FrameLayout最好。这应该也是为何作为顶级View的DecorView也是一个FrameLayout的原因吧。

LinearLayout与RelativeLayout性能PK

细心的同学应该会发现:无论是Eclipse还是Android Studio,新建Blank Activity时默认的layout都是RelativeLayout。查询资料你会发现这是由 android-sdk\tools\templates\activities\BlankActivity\root\res\layout\activity_simple.xml.ftl 这个文件定义的,也就是说这是Google的选择,而非IDE的选择。而且google原文中也提到

A RelativeLayout is a very powerful utility for designing a user interface because it can eliminate nested view groups and keep your layout hierarchy flat, which improves performance. If you find yourself using several nested LinearLayout groups, you may be able to replace them with a single RelativeLayout.

大概的意思是“性能至上”, RelativeLayout 在性能上更好,因为在诸如 ListView 等控件中,使用 LinearLayout 容易产生多层嵌套的布局结构,这在性能上是不好的。而 RelativeLayout 因其原理上的灵活性,通常层级结构都比较扁平,很多使用LinearLayout 的情况都可以用一个 RelativeLayout 来替代,以降低布局的嵌套层级,优化性能。所以从这一点来看,Google比较推荐开发者使用RelativeLayout,因此就将其作为Blank Activity的默认布局了。

有一篇博客写的很不错,通过实际测试以及源码分析比较了两者的性能,总结的也不错!请移步Android中RelativeLayout和LinearLayout性能分析。关于该博文中提到的“DecorView是一个垂直方向的LinearLayout”这句话是不准确的,通过看源码我们可以知道DecorView其实是一个FrameLayout,而LinearLayout是作为它的唯一子元素,构成了我们熟知的DecorView上下两部分(上面是标题栏,下面是内容栏)的结构。而我们经常在Activity中用到的setContentView正是将我们的布局加到了下面部分的内容栏,即content。而content也是一个FrameLayout。

第二,使用<include>、<merge>标签和ViewStub

这部分内容算是比较基础,详细的使用方式可以去看下官方文档,下边主要介绍下使用时需要注意到的点。

<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"
    android:orientation="vertical">

    <include
        android:id="@+id/title_bar"
        layout="@layout/title_bar"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/string"/>
     ......
</LinearLayout>

其中layout="@layout/title_bar"指定了另外的一个布局title_bar.xml,一个应用内很多界面都会用到titlebar,通过这样的方式,这个布局的内容就只需要写一遍就可以了。需要注意的是:1、<include>除了android:id这个属性只支持以 android:layout_开头的属性,而且只要指定了这种属性,那么也必须要指定 android:layout_width 和 android:layout_height,否则会直接报语法错误。2、如果指定了id或者
android:layout_*属性,会直接覆盖导入布局的根布局的相应属性,如果都没有指定则会使用导入布局的根布局的属性。

<merge>标签

在使用<include>标签时,可能会导致重复的布局嵌套,加深布局的层级,从而使得加载变慢,这时我们会用到<merge>标签。所以<merge>标签要和<include>标签配合使用,用来减少布局中重复的层级,而且<merge>标签只能作为<include>指定的布局的根标签使用,这样<merge>标签中包含的子元素,就直接与 <include>平级。
因此在使用<merge>标签时,我们需注意一点:<merge>标签内的子元素会按照<include> 所在布局的布局方式来显示。所以我们在使用的时候需要特别注意布局类型!

ViewStub

在开发过程中,经常会遇到这样一种情况:有些布局在正常情况下不会显示,如网络异常页、空白页等,虽然设置可见性View.GONE,但是在整个界面初始化的时候仍然会将其加载进来,所以增加了初始化时的消耗。这时我们就可以用到ViewStub,它提供了按需加载的功能,在需要的时候才会将ViewStub中的布局加载到内存,所以提高了初始化时的性能,从而也提高了程序的效率。
看源码可以知道,ViewStub继承了View,非常轻量级且宽 /高都是0,因此它本身不参与任何的布局和绘制过程。它的使用方式也很简单,下面是ViewStub的示例:

<ViewStub
        android:id="@+id/stub_import"
        android:inflatedId="@+id/layout_import"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/layout_net_error" />

其中stub_import是ViewStub的id,而layout_import是layout/layout_net_error这个布局的根元素的id。需要加载ViewStub中的布局时,可以使用以下两种方式:
( (ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
View errorLayout = ((ViewStub)findViewById(R.id.stub_import)).inflate();
需要注意的是:ViewStub一旦visible/inflated,就会被它内部的布局替换掉,而它自己也不再是整个布局中的一部分了。所以后面无法再使用ViewStub来控制布局了。
由于ViewStub这种使用后就置空的策略,所以当需要在运行时不止一次的显示和隐藏某个布局时,ViewStub是做不到的,这时就只有使用View的可见性来控制了。另外,目前ViewStub还不支持<merge>标签。

布局优化到这儿就结束了,如果大家还有更好的想法可以提出,我们共同学习,谢谢!

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

推荐阅读更多精彩内容