Android布局性能优化指南

Android布局是应用的重要组成部分,它直接影响到用户的体验。如果布局不合理则会导致内存占用过多且UI卡顿。Android SDK提供了一些工具可以帮助我们快速定位到影响性能的布局问题,一般可从以下几个方面来进行布局优化。

优化布局层次结构

众所周知,复杂的网页加载速度很慢,Android应用也一样,复杂的布局结构也将引起性能问题。下面来说明如何使用工具来检查布局并发现性能瓶颈。

我们知道,应用中的每个组件及布局都需要初始化、测量、绘制等流程,例如使用了嵌套的LinearLayout将会导致更深的View层次,一旦嵌套的LinearLayout使用了layout_weight属性则将导致更长的加载时间,因为每个子控件将被测量两次。这种影响在ListView或GridView中将会更加明显,因为这两个控件的每一个Item都会重用一套布局。

我们将使用Hierarchy Viewer来检查并优化布局。

检查布局

Hierarchy Viewer是Android SDK tools提供的一个工具,它位于sdk/tools/目录下,可以用来分析布局并发现其中的性能瓶颈。需要注意到是使用Hierarchy Viewer时需要保持App正在运行(使用模拟器或连接真机,真机需要支持调试模式),并保持当前应用的进程正常连接。下面举一个例子:

这里写图片描述

上图是一个ListView的行布局结构,最外层是一个水平方向的LinearLayout,其内部左侧是一个ImageView用来显示图片,右侧是一个垂直方向的LinearLayout,该LinearLayout又包含上下两个TextView用来显示文本。这种布局结构在我们日常开发中非常常见,接下来我们检测一下它的布局性能。

打开Hierarchy Viewer工具后,点击“Load View Hierarchy”,结果如下图所示:

这里写图片描述

从上图可以看到,这是一个深度为3层的布局结构,点击每一块布局则会显示其测量、布局、绘制3个过程的时间消耗,如下图所示:

这里写图片描述

它说明使用该布局完全渲染一条列表项的具体耗时为:

  • 测量:0.977ms
  • 布局:0.167ms
  • 绘制:2.717ms

这样开发者就能够清楚地发现哪里是布局的性能瓶颈,接下来就可以针对性地进行优化了。

修改布局

上述布局由于使用了嵌套的LinearLayout才导致性能下降,因此提高性能的办法就是减少布局的层次嵌套,使之扁平化。我们将最外层的LinearLayout替换成RelativeLayout,使用相对布局,可以将原来3层的布局减少为两层。再次使用Hierarchy Viewer查看,结果如下:

这里写图片描述

现在每一行的布局渲染时间消耗为:

  • 测量:0.598ms
  • 布局:0.110ms
  • 绘制:2.146ms

可以看到,性能已经得到了细微的提升,千万别小看这微小的提升,这个布局在ListView中可是会被多次调用的,因此整体性能提升是很可观的。

LinearLayout使用了layout_weight会降低测量的速度,因此在使用权重时务必谨慎,能不用则不用。

使用Lint

我们还可以使用Lint工具来发现布局中可优化的地方,Lint已经内置到Android Studio中,使用非常方便。Lint有以下常用规则:

  • 使用复合Drawable——如果一个LinearLayout包含一个ImageView和一个TextView则使用复合Drawable的方式会更加高效。
  • 合并根布局——如果FrameLayout为根布局且没有背景或内边距等属性,则可以使用marge标签来合并跟布局以提高效率。
  • 无用的叶节点布局——一个布局如果没有子布局且无背景属性,则可以移除它,因为它不会显示出来,移除之后使得布局层次更加扁平且高效。
  • 无用的父布局——如果一个布局(除ScrollView、根布局之外)没有背景等属性,且它的子布局也无兄弟布局,就可以将它本身移除,将其子布局直接移出来。
  • 布局层次过深——布局层次嵌套过多严重影响性能,可考虑使用RelativeLayout或GridLaout来提升性能,建议布局层次深度不要超过10层。

Lint能够自动帮助我们修复一些问题、可以提供修改建议或者跳转到出问题的代码部分,建议大家好好利用Lint这个工具。

使用include重用布局

如果某一种特定的布局结构在应用中出现多次,则可以使用include来重用该布局,提高效率。

定义重用布局

加入我们想重用某个布局,可以单独为它创建一个xml布局,例如,定义一个TitleBar(titlebar.xml),每一个Activity都可以重用它。titlebar.xml内容如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/titlebar_bg"
    tools:showIn="@layout/activity_main" >

    <ImageView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:src="@drawable/gafricalogo" />
</FrameLayout>

上述定义中的tools:showIn属性指定了一个父布局来include该重用布局,此属性将会在编译时移除,它只是为了方便在开发时预览布局效果。

使用include标签

在需要重用布局的地方使用include标签即可引入布局,示例如下:

<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>

也可以为include标签下的布局重写android:layout_*等属性,如:

<include android:id="@+id/news_title"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         layout="@layout/title"/>
使用merge标签

merge标签可以帮助我们消除冗余布局,例如最外层布局是一个垂直方向的LinearLayout,它内部是一个可重用的布局,而这个可重用布局也是一个垂直方向的LinearLayout包含上下两个Button。那么导致的结果是:一个垂直方向的LinearLayout包含另一个垂直方向的LinearLayout,这种冗余嵌套必然延迟UI的加载效率。

对于上述情况,可以使用merge来消除冗余,修改如下:

<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>

按需加载视图

有时候,一些复杂的View只是偶尔才需要显示,那么就可以在需要时再加载它们,这样减小内存占用并提高渲染速度,通常的做法就是使用ViewStub。

定义ViewStub

ViewStub是一个轻量级的view,它不会在布局加载时进行绘制与展示,因此它几乎没有性能开销。每一个ViewStub需要一个android:layout属性来指定要加载的布局。

以下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布局

当你需要加载ViewStub指定的布局时,有两种方法:

  • 通过调用setVisibility(View.VISIBLE)将其设为可见
  • 调用inflate()
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

需要注意的是inflate()方法完成后会返回ViewStub指定的布局,因此不再需要通过findViewById()来得到此布局。

ViewStub一旦被inflate或设为可见,ViewStub元素将不再作为View层次结构的一部分。原来的ViewStub将会被它指定的布局所替代,而这个布局的id就是在ViewStub中指定的android:inflatedId属性的值。而原来的ViewStub的id,即Android:id也将无效。

ViewStub的一个缺点是它指定的布局不支持标签的使用。

使ListView流畅地滑动

保证ListView流畅滑动的关键是应用的主线程没有耗时的事务处理,一些耗时操作如磁盘访问、网络访问、或数据库访问需要使用后台线程。

使用后台线程

使用后台线程可以减轻UI线程的负担,这样UI线程可以专注于UI绘制,保证流程的用户体验。AsyncTask提供了一种简单的后台线程的调用方法,如下示例为使用AsyncTask下载图片,图片下载完成后会显示到视图组件上:

// 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)起,可以通过使用executeOnExecutor()在后台并行处理多个请求。

使用ViewHolder

在ListView滑动时,会频繁调用findViewById(),这将降低性能。即使Adapter返回一个重用的布局,仍然需要找到对应元素并更新它们。一种可以替代频繁调用findViewById()的方式是使用ViewHolder。

ViewHolder对象存储了每一个view组件,最终通过setTag方法保存到Layout的tag字段中,这样就能够快速访问每个组件。ViewHolder类的创建方法如下:

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

填充ViewHolder并把它保存在Layout中:

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);

然后就可以轻松地访问每个视图,而不需要查找,从而节省宝贵的处理时间。

参考文献:Improving Layout Performance

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android 性能优化实战篇 PDF 是一本介绍如何优化 Android 应用性能的电子书。该书通过实践经验和案例分析,向读者提供了一系列提高应用性能的实用技巧和方法。 该书首先介绍了性能优化的基本概念,例如应用启动时间、内存管理、网络请求和绘制性能等方面。之后,通过对真实应用情景的分析,详细介绍了一些常见的性能问题,并给出了相应的解决方案。 对于应用启动时间的优化,该书提供了一些切实可行的建议。例如合理使用启动模式、使用冷启动的启动动画、减少启动时的初始化操作等。这些方法可以明显减少应用的启动时间,提升用户体验。 在内存管理方面,该书介绍了如何减少内存泄漏和优化内存占用。通过使用合适的数据结构、避免频繁的对象创建和销毁、注意资源的主动释放等方法,可以有效地减少内存开销,提高应用的稳定性和响应速度。 对于网络请求的优化,该书提供了一些网络请求的最佳实践。例如使用合适的网络请求库、优化请求的并发性、合理使用缓存等。这些方法可以减少网络请求的时间,提高数据加载速度,提升用户体验。 对于绘制性能的优化,该书介绍了如何优化布局和渲染过程,提高界面的流畅度。例如使用合适的布局容器、避免过度绘制、使用硬件加速等。这些方法可以减少界面卡顿和掉帧现象,提升应用的视觉效果。 总而言之,Android 性能优化实战篇 PDF 提供了一些宝贵的经验和方法,帮助开发者优化应用的性能,提升用户体验。不仅可以帮助开发者理解性能优化的基本原理,还给出了一些具体的解决方案,对于 Android 应用的开发和优化都非常有帮助。 ### 回答2: 《Android性能优化实战篇》是一本针对Android开发者的重要参考书籍,旨在帮助开发者提升应用性能,提高用户体验。该书内容包含以下几个方面: 1. 性能分析工具:介绍了常用的性能分析工具,如Android Profiler、TraceView等。通过使用这些工具,开发者可以监测应用在不同场景下的性能情况,例如CPU占用、内存使用、网络请求等,从而快速定位并解决性能问题。 2. UI性能优化:详细介绍了Android UI渲染原理以及优化方法。通过合理使用View的绘制流程、减少UI层级和View的复杂度、适当使用缓存等方法,可以提升UI渲染的速度,提高应用的响应性。 3. 内存优化:介绍了内存泄漏的原因及排查方法,提供了避免内存泄漏的最佳实践。此外,还介绍了内存管理机制以及如何合理地使用内存,包括优化Bitmap的加载、使用SparseArray替代HashMap等。 4. 网络优化:讨论了网络请求中存在的性能问题,并提供了相应的解决方法。例如,合理使用缓存技术、减少网络请求的频率、使用多线程进行并发请求等。 通过学习《Android性能优化实战篇》,开发者可以更全面地了解和掌握Android性能优化的方法与技巧,并能够针对具体场景中的性能问题进行准确的分析和解决。这对于提高应用的性能和用户体验具有重要意义。 ### 回答3: 《Android性能优化实战篇》是一本介绍如何提升Android应用性能的实战指南,旨在帮助开发人员通过一系列优化技巧和方法,使应用在运行时更加稳定和高效。 该书首先介绍了性能优化的基本概念和原则,包括优化目标、优化思路等。然后详细讲解了Android系统的性能优化核心内容,如内存优化、CPU优化、网络优化等。其中涉及到了一些基本的工具和技术,如内存泄漏的检测与优化、多线程优化、数据缓存等。同时,该书还提供了一些优化实例,通过示例展示了具体的优化方法和效果。 对于开发人员来说,优化应用的性能是一个非常重要的任务,能够提高用户的体验度和应用的竞争力。《Android性能优化实战篇》通过详细的指导和实例,帮助读者快速理解和掌握性能优化的基本方法和技巧,同时提供了大量实战经验和案例,让读者能够更加深入地了解优化的过程和效果。 总而言之,该书是一本非常实用的Android性能优化指南,对于开发人员来说是一本必备的参考书籍。通过阅读该书,开发人员能够系统地学习和掌握Android应用的性能优化方法,提高应用的性能和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值