浅聊Android布局优化

作者: 追云_似梦 | 来源:发表于2016-06-26 01:47 被阅读603次

    前言

    在Android开发中,常用的布局就只有FrameLayout&LinearLayout&RelativeLayout,这些布局由于年代恒久远,一直流传下来。但是也存在着一些遗留问题,在Activity展开布局时用到setContentView()方法也许大家并不陌生。在使用这个方法时发生了什么呢?

    现在大多数开发使用的都是android.support.v7的包吧,具体的Activity实现,在android.support.v7.app.AppCompatDelegateImplV7 中setContentView()代码如下:

    @Override 
    public void setContentView(int resId) { 
            ensureSubDecor(); 
            ViewGroup contentParent = (ViewGroup)       
            mSubDecor.findViewById(android.R.id.content);         
            contentParent.removeAllViews(); 
            LayoutInflater.from(mContext).inflate(resId, contentParent); 
            mOriginalWindowCallback.onContentChanged(); 
     }
    

    首先contentParent会移除所有的布局文件,然后LayoutInflater会根据当前的context来展开布局并填充到contentParent中。
    可以看到一个布局在Activity中展开取决于inflate的resId,再看看inflater相关的代码:

      public View inflate(XmlPullParser parser, @Nullable ViewGroup root) { 
          return inflate(parser, root, root != null); 
      } 
      public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { 
      final Resources res = getContext().getResources(); if (DEBUG) { 
          Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } 
          final XmlResourceParser parser = res.getLayout(resource); 
          try { 
                return inflate(parser, root, attachToRoot); 
          } finally { 
                parser.close(); 
          } 
      }  
    

    首先Android读取应用的资源数据(APK文件内,存储在内部存储或者SD卡中);
    其次使用XmlResourceParaser来解析资源数据,展开布局;
    最后将展开后的布局添加到Activity的顶层视图。

    这里花费的时间取决于布局的复杂性,资源数据越大,解析越慢,而更多的组件实例也让布局实例化变慢。

    现状

    一般布局通常在Activity的onCreate()方法中展开,这里花费的时间会直接影响Activity的启动时间。
    所以尽量减少布局花费的时间可以提升应用的界面流畅度。
    现在大多数的方案,减少创建对象数量,使用<merge>标签减少<FrameLayout>包裹的布局对象,或者是使用<ViewStub>推迟创建对象。
    不过现在产品界面复杂,光靠这几个方法也不能完全解决对象的数量创建过度的问题,比如一些布局需要按照比例来适配,那么只能利用LinearLayout的layout_weight布局属性来实现。
    其中会增加一大堆布局对象,显然不符合我们对布局优化的需求。
    Google官方考虑到这种需求,也发布了解决这些问题的包Android Support Library(需要翻墙)。其中包含一个Percent Support Library 顾名思义,是一个支持百分比布局的包。

    在Gradle中配置如下:

        com.android.support:percent:23.3.0
    

    即可获取Android百分比布局的包。关于这个包的使用,可以参考文章《Percent Support Library: Bring dimension in % to RelativeLayout and FrameLayout》 具体的使用就不再本文追叙了。毕竟有一篇这么好的参考文章,我就不添乱了。

    另外,Github上也有相关的开源示例项目:Android Precent Support lib Sample 请自行查看。

    优化布局的使用建议

    避免使用嵌套线性布局

    嵌套的线性布局存在两个问题:

    • 嵌套线性布局会深化布局层次,从而导致布局和按键处理变慢;
    • 造成很多用于定位的无用对象;

    解决方案:

    • 对于不要求布局中的组件需要使用百分比的,可以优先考虑使用RelativeLayout来替代LinearLayout,减少布局中的组件数量。
    • 对于要求使用百分比的,可以考虑使用PercentRelativeLayout。

    使用<merge>标签

    通常,在使用<merge>标签来合并布局时,其布局的顶层元素是一个FrameLayout可以考虑使用<merge>标签,在Activity中,其默认的顶层布局就是一个FrameLayout,对应的id为android.R.id.content

    使用<include>重用布局

    重用布局,在布局展开时,<include>的布局是动态处理的,因为包含是在编译时完成的,但是Android并不知道这里包含的布局具体对应哪种屏幕情况(比如横屏和竖屏布局)。
    其实严格来讲,<include>不属于布局的优化,属于代码结构的优化,可以帮助开发人员减少很多重复的代码,减少包的体积。

    使用<ViewStub>推迟加载布局

    推迟初始化是一个很好的技术,可以推迟实例化在需要用到的时候再实例化,提高应用的性能,还能节省内存。关于ViewStub的使用有很多文章介绍,这里本着不添乱的精神,简单的介绍一下<ViewStub><include> 的区别。
    首先,这两者都需要使用android:layout 属性来定义需要引入的布局文件,其次<include>在加入布局文件不能实现延迟加载,而<ViewStub>在未展开布局之前在视图中是不可见的,需要调用inflate()或者setViewVisibility(View.VISIBLE)来显示,并且在显示的同时,会在父布局中移除<ViewStub>,由展开的布局所替代。

    此外,<ViewStub>中还得定义一个android:inflatedId供Activity找到展开后的布局实例。

    布局优化工具

    为了优化已有的布局,Android SDK提供了两个易于使用的工具:HierarchyViewer 和 LayoutOpt。这些工具位于SDK的tools目录。
    不过在使用HierarchyViewer时,我就碰到一个问题,就是怎么都不显示所谓的高端大气上档次的布局层次依赖关系图,解决办法:集成ViewServer开源项目,地址:https://github.com/romainguy/ViewServer

    另外,在最新的sdk中,LayoutOpt貌似被lint工具给替代了,这个工具比较牛逼的地方就在于静态代码检查,会提出一些优化方案,比如使用HashMap的地方,会告诉你使用SparseArray,一些布局优化的建议也会给出。

    END

    由于个人能力有限,文中如有疏漏之处还请斧正多多交流

    相关文章

      网友评论

      本文标题:浅聊Android布局优化

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