Android性能优化之提高应用响应速度(了解ViewStub和推迟视图的初始化)

为什么需要推迟视图初始化

这里谈谈为什么要推迟视图的初始化. 假设这样一个情况, 如果手机的界面包含大量的视图组件,而且数据大多都来源于服务器. 这就意味着一进入这个界面,应用就会在onCreate()方法中去初始化为数不少的layout资源, 而初始化View是一个比较耗时耗资源的操作. 然后, 应用进行几个网络链接,去获取数据回来更新填充View中的各个组件. 这样算起来, 用户从进入某个Activity, 到真的看到该Activity的视图, 所需等待的时间确实不短.  而且要知道, 应用中如果某个操作的请求超过5秒, 广播的处理时间超过10秒,
系统就会认为该请求是无法处理的, 弹出可恶的ANR弹窗,提示用户是继续等待or强制关闭这个应用. 这样用户的体验就很差了. 用户一旦不爽,就很可能卸载了这个应用. 真乃" 一念天堂,一年地狱."

当然我们可以通过不断优化应用中布局文件的设计,比如精简layout中的层级关系,减少不必要的组件, 尽量用RelativeLayout去代替嵌套的LinearLayout等等,达到我们减轻初始化View, 提高应用响应速度的目的.这些我们都会, 然而,这次我们了解另一种方案, 使用ViewStub推迟视图初始化.

我们日常应用环境, 其实随处可见这种设计思想. 打开一个网站的页面,我们会看到一个Loading的提示框. 打开手机中某款应用, 显示一个可爱的加载动画, 或者提示页面等等. 然后应用在"背后" 努力的初始化资源, 甚至还打开几个子线程去服务器读数据回来客户端更新数据等等. 用户可不关心应用实际上在做什么, 但也不会愿意一直看着白屏在那里等待.就像你在女票的楼下等女票一样, 等别人即使是多一秒, 心里也总是焦急难熬. 这就有必要进行推迟视图的初始化了.

了解ViewStub

OK, 扯多了. 我们直接来看ViewStub是何物. 通常如果一个类不是一万几千行的代码, 我是非常推荐去把类源码读完的, 这样能深入的了解一个类的作用.如果源码太多,时间不允许, 就选择需要的几个方法去读, 或者快速浏览一下类的结构方法.

ViewStub作为View的子类,是一种非常轻量化,几乎不占用内存的组件.也因为这个特点,它非常适合用来放在推迟视图的初始化中使用.在Activity的onCreate()中,系统会自动初始化ViewStub组件, 这个时候用户只会看到ViewStub.  然后应用进行数据的读取或处理, 觉得准备就绪, 就利用ViewStub.inflate()方法去初始化, 这个方法会返回真正的View对象, 也就是应用所真正呈现给用户的界面. 一旦初始化,ViewStub就会被标记为可回收的资源, 从而会被系统回收掉.

下面是一个完整的用法:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

   <ViewStub
       android:id="@+id/stub_main"
       android:inflatedId="@+id/stubid"
       android:layout="@layout/act_main"
       android:layout_width="400dp"
       android:layout_height="200dp"
       />

</RelativeLayout>

act_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/pic_default"
        />

</RelativeLayout>

MainActivity:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewStub = (ViewStub)findViewById(R.id.stub_main); <span style="white-space:pre">	</span>//获取ViewStub
        mView = viewStub.inflate();<span style="white-space:pre">				</span>//调用ViewStub.inflate()获取真正的View
        ImageView imageView = mView.findViewById(R.i.image);

    }

只是展示用法,因为用了很简单的例子, 应该不难理解吧. 首先是MainActivity初始化一个非常简单的界面activity_main.xml, 这个界面只有一个ViewStub,然后ViewStub里面通过inflateId作为唯一标记引用了act_main.xml. 在代码里通过viewStub.inflate() 获取这个act_main.xml的初始化View对象. 所以整个过程, 用户一开始看到的是ViewStub, 然后才是ImageView. 这个就是推迟视图初始化的过程.

值得注意的是, 所被初始化的act_main.xml.的大小,和所处同一个parent的ViewStub的具有相同大小.也可以这么理解,ViewStub的大小,决定了viewStub.inflate()所返回的View对象的长宽大小.如上面示例所示, ViewStub是宽为400dip,长200dip,因此act_main.xml的大小也是宽为400dip,长200dip.

借鉴了官方的这种思路,我们不免会觉得ViewStub所展示出来的界面太单调了.于是我们可以用自己的View去代替ViewStub. 只要我们保证一个加载页面的足够简洁(比如只是一个加载动画,或者一个温馨的提示界面等等),我们甚至不必去使用这个View的findViewById()操作.仅仅是加载一个足够简洁,不占用什么内存的View. 进一步想, 我们还能保持这个View能被各个Activity所引用,而不需要重复创建这个加载的View. 那么应用牺牲非常少的内存,也是值得的. 这个思路, 就是现在几乎所有常见应用进入某个界面都能看到统一的加载界面的设计思路.
没有最好,只有更好. 这里仅仅提供其中一种的思路, 不能说是最优方案,仅供参考.

希望本次性能优化系列的文章, 能给你一点点帮助哦~ 感谢你的阅读.

抽时间看了ViewStub的源码, 官方的注释已经非常全面的介绍了该类. 于是将我直接翻译了该类的注释,有兴趣可以看看哦.

/**
 * (个人认为,这个类只读官方的注释足以了解,源代码量也不大,但不必深究inflate的过程.只需要懂得ViewStub为何物,以及如何使用即可.)
 *
 *ViewStub是不可见的,不占用内存的View视图. (当程序初始化视图时)它可以用来推迟初始化视图里面的各种layout资源.
 *
 * A ViewStub is an invisible, zero-sized View that can be used to lazily inflate
 * layout resources at runtime.
 *
 * 当ViewStub被设定为可见或者开始初始化视图时,layout资源开始被初始化(这个时候,真正要使用的各种layout资源才开始占用内存).
 * 这时,ViewStub会被相同的parent中所初始化的View代替.因此,ViewStub的存在直至设置setVisibility(VISIBLE)或者调用方法去
 * 初始化view(比如通过使用ViewStub.inflate()方法)时就会结束.
 *
 * When a ViewStub is made visible, or when {@link #inflate()}  is invoked, the layout resource
 * is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.
 * Therefore, the ViewStub exists in the view hierarchy until {@link #setVisibility(int)} or
 * {@link #inflate()} is invoked.
 *
 * 初始化的View会被添加到与ViewStub相同parent的容器中.类似的,你能通过使用ViewStub的inflateId属性来定义或者覆盖视图View的id.
 * (就是说,将要初始化的View的ID放在ViewStub的布局文件中,比如android:inflatedId="@+id/subTree".然后使用ViewStub的inflate()方法,你就能获取这个View.)
 * 下面是一个布局文件的示例.
 *
 * The inflated View is added to the ViewStub's parent with the ViewStub's layout
 * parameters. Similarly, you can define/override the inflate View's id by using the
 * ViewStub's inflatedId property. For instance:
 *
 * <pre>
 *     <ViewStub android:id="@+id/stub"
 *               android:inflatedId="@+id/subTree"
 *               android:layout="@layout/mySubTree"
 *               android:layout_width="120dip"
 *               android:layout_height="40dip" />
 * </pre>
 *
 * 上面示例中,ViewStub通过使用"stub"作为id,能被应用所获取并使用.在"mySubTree"这个view里被初始化后,ViewStub就会从容器中被移除(即被标记为系统可回收的资源)
 * 而被初始化的View通过使用inflateId为subTree的这个属性,又能被初始化为mySubTree的View.可见,inflateId(值为subTree)是inflate过程的唯一标记.而最后被初始化的
 * mySubTree这个布局文件,所占用的大小跟ViewStub是完全一样的,都是android:layout_width="120dip", android:layout_height="40dip".
 *
 * The ViewStub thus defined can be found using the id "stub." After inflation of
 * the layout resource "mySubTree," the ViewStub is removed from its parent. The
 * View created by inflating the layout resource "mySubTree" can be found using the
 * id "subTree," specified by the inflatedId property. The inflated View is finally
 * assigned a width of 120dip and a height of 40dip.
 *
 * The preferred way to perform the inflation of the layout resource is the following:
 *
 * 下面是在上面代码示例的基础上,去获取ViewStub和通过ViewStub推迟初始化的方法:
 *
 * <pre>
 *     ViewStub stub = (ViewStub) findViewById(R.id.stub);
 *     View inflated = stub.inflate();
 * </pre>
 *
 * 当ViewStub的inflate()方法被调用时,ViewStub会被所初始化的View所代替,并且该方法会返回一个View对象.
 * 这使得应用程序能获取视图的引用而又不必执行额外的findViewById()操作.(因为findViewById()操作也是占用一定系统资源的.)
 *
 * When {@link #inflate()} is invoked, the ViewStub is replaced by the inflated View
 * and the inflated View is returned. This lets applications get a reference to the
 * inflated View without executing an extra findViewById().
 *
 * @attr ref android.R.styleable#ViewStub_inflatedId
 * @attr ref android.R.styleable#ViewStub_layout
 */
@RemoteView
public final class ViewStub extends View {
    //...
}
时间: 2024-11-08 14:25:09

Android性能优化之提高应用响应速度(了解ViewStub和推迟视图的初始化)的相关文章

Android性能优化之提高ListView性能的技巧

ListView优化一直是一个老生常谈的问题.无论是面试还是寻常的开发中,ListView永远不会被忽略掉,那么这篇文章我们来看看怎样最大化的优化ListView的性能. 1.在adapter中的getView方法中尽量少使用逻辑 2.尽最大可能避免GC 3.滑动的时候不载入图片 4.将ListView的scrollingCache和animateCache设置为false 5.item的布局层级越烧越好 6.使用ViewHolder 1.在adapter中的getView方法中尽量少使用逻辑

Android性能优化的一些理解

前言 Android性能优化对Android程序的维护和拓展是有很大帮助的,我们知道Android手机不管是内存还是CPU都无法同PC相比,这也就意味着我们必须要谨慎的去使用内存和CPU资源.因为稍稍不注意可能就会引发诸如OOM.ANR.内存泄漏等问题,所以熟悉Android性能优化的几个方法可以有效地提高应用程序的性能,我们可能都能说出一些性能优化的方法,比如布局优化.绘制优化.线程优化等等,但是可能我们会忽视某些小细节,比如布局优化我们可能都知道可以使用< include >来减少布局的层

Android 性能优化

上周四参加了MDCC大会的 Android,我比较关注的5R,做一个安静的app,图片缓存以及React Native For Android,其中很大一部分的内容都是讲的性能优化,后续还会给大家带来一篇React Native ,下面就来总结一下Android性能优化方面的内容! Reduce Cache/Drawable load in demand bitmap - scale/format Reuse pools inBitmaps convertView onDraw / for Re

Android性能优化总结(转)

前言 性能优化本身是一个很大的主题,涵盖程序的方方面面,任何不慎的操作,都有可能对性能造成比较大的影响,要知道程序的性能是可以累加的,多处的性能低下, 会影响整体的性能,其后果可能也是多方面的,本文总结了目前工作中,所需要知道的大部分性能优化点,一部分个人总结,一部分来自于互联网.但整体上,都是 提纲性的,并没有列出具体的实例,因为写这方面主题的达人实在太多了,所以,我得站在巨人的肩膀上,具体细节,请参考对应的链接. 性能低下的现象 游戏:界面很卡,FPS低 搜索性能差 服务器响应速度慢 OS:

Android性能优化典范第二季

Google前几天刚发布了Android性能优化典范第2季的课程,一共20个短视频,包括的内容大致有:电量优化,网络优化,Wear上如何做优化,使用对象池来提高效率,LRU Cache,Bitmap的缩放,缓存,重用,PNG压缩,自定义View的性能,提升设置alpha之后View的渲染性能,以及Lint,StictMode等等工具的使用技巧.关于该课程的介绍可以查看该地址:https://www.udacity.com/course/android-performance--ud825. 未完

Android 性能优化探究

使用ViewStub动态加载布局,避免一些不经常的视图长期握住引用: ViewStub的一些特点: 1. ViewStub只能Inflate一次,之后ViewStub对象被置空:某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了. 2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中. 基于以上的特点,那么可以考虑使用ViewStub的情况有: 1. 在程序的运行期间,某个布局在Inf

Android性能优化典范(二)

原文出处: 胡凯的博客(@胡凯me)   欢迎分享原创到伯乐头条 Google前几天刚发布了Android性能优化典范第2季的课程,一共20个短视频,包括的内容大致有:电量优化,网络优化,Wear上如何做优化,使用对象池来提高效率,LRU Cache,Bitmap的缩放,缓存,重用,PNG压缩,自定义View的性能,提升设置alpha之后View的渲染性能,以及Lint,StictMode等等工具的使用技巧. 下面是对这些课程的总结摘要,认知有限,理解偏差的地方请多多指教! 1)Battery

Android性能优化方法(八)

Android SDK tools目录下提供一个观察布局的工具,层级观察器(Hierarchy Viewer).Hierarchy Viewer工具是一个非常好的布局优化工具,同时,你也可以通过它学习他人的布局.应该说是一个非常实用的工具. AD:WOT2014:用户标签系统与用户数据化运营培训专场 层级观察器(Hierarchy Viewer): Android SDK tools目录下提供一个观察布局的工具,层级观察器(Hierarchy Viewer).Hierarchy Viewer工具

Android性能优化系列之apk瘦身

Android性能优化系列之布局优化 Android性能优化系列之内存优化 为什么APK要瘦身.APK越大,在下载安装过程中,他们耗费的流量会越多,安装等待时间也会越长:对于产品本身,意味着下载转化率会越低(因为竞品中,用户有更多机会选择那个体验最好,功能最多,性能最好,包最小的),所以apk的瘦身优化也很重要,本篇博客将讲述apk瘦身的相关内容. 包体分析 在Android Studio工具栏里,打开build–>Analyze APK, 选择要分析的APK包 可以看到占用空间的主要是代码.图