• Android中,如何提升Layout的性能?


    Layout 是 Android 应用中直接影响用户体验的关键部分。如果实现的不好,你的 Layout 会导致程序非常占用内存并且 UI 运行缓慢。Android SDK 带有帮助你找到 Layout 性能问题的工具。

    主题一:优化Layout层级

    一个常见的误区是,用最基础的Layout结构可以提高Layout的性能。然而,因为程序的每个组件和Layout都需要经过初始化、布局和绘制的过程,如果布局嵌套导致层级过深,上面的初始化、布局和绘制操作就更加耗时。例如,使用嵌套的LinearLayout可能会使得View的层级结构过深,此外嵌套使用了 layout_weight参数的LinearLayout的计算量会尤其大,因为每个子元素都需要被测量两次。这对需要多次重复inflate的Layout尤其需要注意,比如嵌套在 ListView或GridView时。

    In this lesson you'll learn to use Hierarchy Viewer and Layoutopt to examine and optimize your layout. 使用两个工具:Hierarchy Viewer和Layoutopt。

    如何审视自己设计的Layout?

    Android SDK 工具箱中有一个叫做 Hierarchy Viewer 的工具,能够在程序运行时分析 Layout。你可以用这个工具找到 Layout 的性能瓶颈。

    Hierarchy Viewer 会让你选择设备或者模拟器上正在运行的进程,然后显示其 Layout 的树型结构。每个块上的交通灯分别代表了它在测量、布局和绘画时的性能,帮你找出瓶颈部分。比如,下图是 ListView 中一个列表项的Layout。列表项里,左边放一个小位图,右边是两个层叠的文字。像这种需要被多次 inflate 的 Layout ,优化它们会有事半功倍的效果。

    The hierarchyviewer tool is available in <sdk>/tools/. Click Load View Hierarchy to view the layout hierarchy of the selected component.

    找到UI性能瓶颈了,如何修正Layout?

    上面的 Layout 由于有这个嵌套的 LinearLayout 导致性能太慢,可能的解决办法是将 Layout 层级扁平化 - 变浅变宽,而不是又窄又深。RelativeaLayout 作为根节点时就可以达到目的。所以,当换成基于 RelativeLayout 的设计时,你的 Layout 变成了两层。新的Layout变成如下形式:

    可能看起来是很小的进步,但是由于它对列表中每个项都有效,这个时间要翻倍。这个时间的主要差异是由于在 LinearLayout 中使用 layout_weight 所致,因为会减慢“测量”的速度。这只是一个正确使用各种 Layout 的例子,当你使用 layout_weight 时有必要慎重。

    如何使用Lint工具辅助检测?

    运行 Lint 工具来检查 Layout 可能的优化方法,是个很好的实践。Lint 已经取代了Layoutopt工具,它拥有更强大的功能。

    从Eclipse中如何启动Lint?如下方式均可以:

    Lint中包含如下检测规则:

    使用compound drawable — 用一个compound drawable 替代一个包含 ImageView 和 TextView 的 LinearLayout 会更有效率。

    合并根 frame — 如果 FrameLayout 是 Layout 的根节点,并且没有使用 padding 或者背景等,那么用 merge 标签替代他们会稍微高效些。

    没用的子节点 — 一个没有子节点或者背景的 Layout 应该被去掉,来获得更扁平的层级。

    没用的父节点 — 一个节点如果没有兄弟节点,并且它不是 ScrollView 或根节点,没有背景,这样的节点应该直接被子节点取代,来获得更扁平的层级。

    太深的 Layout — Layout 的嵌套层数太深对性能有很大影响。尝试使用更扁平的 Layout ,比如 RelativeLayout 或 GridLayout 来提高性能。一般最多不超过10层。

    主题二:使用<include>标签重复利用布局

    虽然 Android 提供很多小的可重用的交互组件,你仍然可能需要重用复杂一点的组件,这也许会用到 Layout。为了高效重用整个的 Layout,你可以使用 <include/> 和 <merge/> 标签把其他 Layout 嵌入当前 Layout。

    重用 Layout 非常强大,它让你可以创建复杂的可重用 Layout。比如,一个 yes/no 按钮面板,或者带有文字的自定义进度条。这也意味着,任何在多个 Layout 中重复出现的元素可以被提取出来,被单独管理,再添加到 Layout 中。所以,虽然可以添加一个自定义 View 来实现单独的 UI 组件,你可以更简单的直接重用某个 Layout 文件。

    如何创建可重用的Layout?

    如果你已经知道你需要重用的 Layout,就先创建一个新的 XML 文件并定义 Layout 。比如,以下是一个来自 G-Kenya codelab 的 Layout,定义了一个需要添加到每个 Activity 中的标题栏(titlebar.xml):

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width=”match_parent”
        android:layout_height="wrap_content"
        android:background="@color/titlebar_bg">
        <ImageView android:layout_width="wrap_content"
                   android:layout_height="wrap_content" 
                   android:src="@drawable/gafricalogo" />
    </FrameLayout>

    其中:根节点的View类型就是你想要的添加进入的Layout。

    如何添加到指定Layout中,使用<include>标签

    使用 <include> 标签,可以在 Layout 中添加可重用的组件。比如,这里有一个来自 G-Kenya codelab 的 Layout 需要包含上面的那个标题栏:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width=”match_parent”
        android:layout_height=”match_parent”
        android:background="@color/app_bg"
        android:gravity="center_horizontal">
        <include layout="@layout/titlebar"/>
        <TextView android:layout_width=”match_parent”
                  android:layout_height="wrap_content"
                  android:text="@string/hello"
                  android:padding="10dp" />
        ...
    </LinearLayout>

    你也可以覆写被添加的Layout的所有Layout参数(任何android:layout_* 属性),通过在<include/>中声明他们来完成。比如:

    <include android:id=”@+id/news_title”
             android:layout_width=”match_parent”
             android:layout_height=”match_parent”
             layout=”@layout/title”/>

    但必须指出的是:如果想要覆写被加入Layout的属性,必须先覆写其layout_width和layout_height属性。

    另一个方式:使用<merge>标签

    <merge /> 标签在你嵌套 Layout 时取消了 UI 层级中冗余的 ViewGroup 。比如,如果你有一个 Layout 是一个竖直方向的 LinearLayout,其中包含两个连续的 View 可以在别的 Layout 中重用,那么你会做一个 LinearLayout 来包含这两个 View ,以便重用。不过,当使用一个 LinearLayout 作为另一个 LinearLayout 的根节点时,这种嵌套 LinearLayout 的方式除了减慢你的 UI 性能外没有任何意义。

    为了避免这种情况,可以使用<merge>元素来替代可重用Layout的根节点。

    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <Button
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content"
            android:text="@string/add"/>
        <Button
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content"
            android:text="@string/delete"/>
    </merge>

    现在,当你要将这个 Layout 包含到另一个 Layout 中时(并且使用了 <include/> 标签),系统会忽略 <merge> 标签,直接把两个 Button 放到 Layout 中 <include> 的所在位置。

    主题三:按需加载布局

    主题四:优化ListView滑动性能

    保持程序流畅的关键,是让主线程(UI 线程)不要进行大量运算。你要确保在其他线程执行磁盘读写、网络读写或是 SQL 操作等。为了测试你的应用的状态,你可以启用 StrictMode。

    使用后台线程加载数据

    Using a background thread ("worker thread") removes strain from the main thread so it can focus on drawing the UI.In many cases, using AsyncTask provides a simple way to perform your work outside the main thread. UI线程仅仅做Layout的绘制,“worker thread”运行后台任务。

    // Using an AsyncTask to load the slow images in a background thread
    new AsyncTask<ViewHolder, Void, Bitmap>() {
        private ViewHolder v;
        @Override
        protected Bitmap doInBackground(ViewHolder... params) {
            v = params[0];
            return mFakeImageLoader.getImage();
        }
        @Override
        protected void onPostExecute(Bitmap result) {
            super.onPostExecute(result);
            if (v.position == position) {
                // If this item hasn't been recycled already, hide the
                // progress and set and show the image
                v.progress.setVisibility(View.GONE);
                v.icon.setVisibility(View.VISIBLE);
                v.icon.setImageBitmap(result);
            }
        }
    }.execute(holder);

    这个行为是全局的,这意味着你不需要考虑自己定义线程池的事情。从 Android 3.0 (API level 11) 开始, AsyncTask 有个新特性,那就是它可以在多个 CPU 核上运行。你可以调用 executeOnExecutor()而不是execute(),前者可以根据CPU的核心数来触发多个任务同时进行。

    使用ViewHolder填入视图对象

    你的代码可能在 ListView 滑动时经常使用 findViewById(),这样会降低性能。即使是 Adapter 返回一个用于回收的 inflate 后的视图,你仍然需要查看这个元素并更新它。避免频繁调用 findViewById() 的方法之一,就是使用 ViewHolder(视图占位符)的设计模式。

    一个 ViewHolder 对象存储了他的标签下的每个视图。这样你不用频繁查找这个元素。第一,你需要创建一个类来存储你会用到的视图。比如:

    static class ViewHolder {
      TextView text;
      TextView timestamp;
      ImageView icon;
      ProgressBar progress;
      int position;
    }

    在Layout类中生成一个ViewHolder实例:

    ViewHolder holder = new ViewHolder();
    holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
    holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
    holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
    holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
    convertView.setTag(holder);
    

      

    这样你就可以轻松获取每个视图,而不是使用 findViewById() 来不断查找子视图,节省了宝贵的运算时间。

  • 相关阅读:
    基于SpringBoot实现请求的数据权限验证 规格严格
    PostgreSQL大版本升级(pg_upgrade)11.7升12.2 规格严格
    PostgreSQL还原数据库时出现问题:pg_restore: [archiver] unsupported version (1.13) in file header 规格严格
    mysql 如何去除表数据当中的回车符,换行符,空格和水平制表符?
    mysql 如何修改主键起始值?
    ASCII对照表
    [Unit testing RxJS] Test error handling with marbles
    [Typescript] 49. Medium PartialByKeys
    [Typescript] 46. Medium PickByType
    [Typescript] 48. Medium EndsWith
  • 原文地址:https://www.cnblogs.com/CVstyle/p/6384129.html
Copyright © 2020-2023  润新知