tomorrow.cyz@gmail.com

  本文仅关注Android 布局优化,不讨论布局之外的优化。

  布局优化是Android应用优化的一个重要方面,它可以减少应用在UI布局上layout,measure和onDraw的时间,从而减少渲染

时间。布局优化的原则非常简单,就是减少嵌套层级,尽量让布局扁平化。涉及到的知识也不多,基本上就是官方的Improving Layout Performance

很简单的几篇文章就够了。

  更多的功底体现在理解你的需求,更好地组织功能,布局和代码上,以及,抠细节。

从Android Lint开始

  Android Lint是个静态代码分析工具,无需运行,即可对项目进行扫描,分析潜在的问题。

应该把它当做App性能优化的第一步,以及日常代码规范的一部分。它的设置见

https://developer.android.com/training/improving-layouts/optimizing-layout.html#Lint

使用很简单,执行Android Studio的Analysis–>Inspect Code即可,执行完,inspection窗口就

可以显示潜在问题项。

使用Hierarchy Viewer分析布局

  Hierarchy Viewer可以帮助开发者debug和优化布局,它以可视化的形式展现布局结构和属性,可以结合debug帮助观察特定UI

对象进行invalidate和requestLayout的过程,还可以导出displayList。

  Hierarchy Viewer已经集成到Android Studio 中,点选Android Device Monitor可以打开DDMS,在右上角靠近DDMS图标的地方,

有个Hierarchy Viewer图标,点击可以打开Hierarchy Viewer。如果找不到这个图标,可以reset layout。

  除了在Android Studio中使用Hierarchy Viewer,还可以使用standalone的Hierarchy View,在Android/Sdk/tools目录下,直接

运行。Standalone的版本有像素视图,可以观察细节。

  Hierarchy Viewer右上角的三个球图标可以获取节点layout的时间,点击后,当前选中的节点以及它的子节点都会显示出Measure,

Layout,onDraw时间,在节点上显示三个球,分别对应这三个时间。红色和黄色代表相对性能较差的节点,相对性能较差不不一定

有问题,但值得你花更多时间关注。

使用include标签重用布局

  使用include可以重用你的UI部分(相当于把他们组件化),比如,App有N个界面,每个界面都需要一个TitleBar,可以把TitleBar

独立出来,在各个页面中include。

  这样做的好处是可以减少维护成本,只要改一个地方,就全局生效了。另外,如果你想要一个其它风格的TitleBar,在主布局修改

include的layout src就可以了。

  如果你所有的界面TitleBar还有细微的差别,将共性的东西抽出来,在里面留出可以定制的部分,让每个界面去客制化。

当然,重用也有度,如果差别太大,重用会引入一些太多不需要的组件,就不要重用。

  include的使用很简单,直接使用include标签,设置layout属性就可以。如下

通常在titlebar这个布局文件里面设置layout_height,layout_width等属性,也可以在include标签里面指定覆盖它。

<include layout="@layout/titlebar" 
               android:layout_width="match_parent"
               android:layout_height="40dp"
               android:id="@+id/main_title"/>

使用merge标签

  很多开发人员使用include标签的时候,上来就先LinearLayout或者RelativeLayout,作为容器。然后在容器里面再加控件。

其实在很多情况下,这个作为容器的标签是可以优化掉的。merge标签就是用来在此时帮助消除重复的container的。

比如

    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <Button ..../>
        <Button ..../>
    </merge>

直接使用include的parent节点作为Button的container。

使用ViewStub实现需要时加载

  有时候,你UI中的某些元素,你只有在某些情况下才会展现。比如某些设置的高级功能,而你又想将所以的设置放在一个页面。

这个时候可以考虑放置一个”高级“或者”更多“标签,点击一下,把剩下的选项show出来。大多时间,你不会展示这些高级选项。

很多开发者第一时间想到setVisibility来实现。

  相比之下,ViewStub是更好的选择,setVisibility(GONE)虽然不参与排版,但是控件会申请内存,要初始化,findViewById的

遍历也要加深,这些都是代价。ViewStub就像它的名字一样,仅在那个地方打个桩,不仅不参与绘制,UI组件也不会初始化。在你需要展示

的时候,才会初始化控件,分配内存。

  ViewStub有个限制,就是不支持在它的layout里面使用merge标签。

ViewStub的使用如下:

  布局里面声明

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

  代码里面加载

    ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
    // or
    View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

  相应的,在加载的时候再去获取子控件

    Button btn = (Button)importPanel.findViewById(R.id.btn);

一些值得考虑的地方

  • 很多开发人员喜欢通过引入一个组件改变多个控件的属性,这种情况的确存在,比如在一个横排的LinearLayout中突然插入一个

    竖排的LinearLayout,但是更多的情况,是可以通过巧妙设计控件的属性来达到。所以,碰到这种情况,值得仔细推敲下。甚至

    在这个例子中,也有很多情况下可以通过RelativeLayout来实现。

  • 布局的原则是尽量扁平,减少嵌套

  • 减少LinearLayout layout_height参数的使用,Android官方已经明确这个属性会降低layout的效率。

  • 如果只是一次性弹出来的部分,比如一个提示界面,适合用动态的方式实现,比如用PopWindow,或者addView/removeView,而不

    要当成布局的一部分,用setVisibility来实现。

  • 有些应用在不同场合下会显示不同的btn,这时候可以考虑只用一个ImageButton,动态设置ImageButton的图片(setImageResource)。

  • 对LinearLayout的嵌套思考再思考

  • 使用compound drawables(可以理解为图文按钮),比如

    更多  >>
    

    可以通过setDrawableRight来实现,一个组件就可以,而不是如下三个组件

    LinearLayout
    
        TextView
        
        ImageView
    

    这一点Android Lint会有提示

  • 图片需要点击事件响应的时候,如果把外层的LinearLayout优化掉,要加大padding,以   增加点击响应范围,避免点击效果下降

  • 建议深刻理解RelativeLayout布局

参考

  1. http://blog.csdn.net/ljz2009y/article/details/22690935

  2. http://blog.csdn.net/ddna/article/details/5527072

  3. https://developer.android.com/training/improving-layouts/optimizing-layout.html


dlmu2001

stay foolish, stay hungry!