一、 问题描述
目前市面上很多安卓应用存在一个问题,就是应用启动后要么会黑屏较长一段时间,要么会启动一个广告页或导航页,并且加载较长时间,这是一种相当不好的用户体验,应该予以改进。
二、 改进方法
改进启动和加载速度能很大程度提升用户体验,对于启动时间每个用户的忍耐程度不同,对我而言,如果时间超过1.5秒对应用的粘度会下降一半,也就是删除应用的概率也会提升50%,因此改进启动速度非常重要。改进方法较多,我将这些方法大致分类,并根据自己的开发实践加以陈述。
1. 只做必要的事
启动时间一刻千金,我们的每一个操作都应该分秒必争,一定要把启动过程中做的每一件事都分析清楚,是否一定要在启动时去做,把所有启动时间都留给必要的事。如果一定想要把一些不是很必要的任务也放在启动时去执行,可以把他们放在一个子线程中去执行。
举个简单的例子来区分是否是必要任务。MainActivity加载一个Fragment,有两个Fragment可以选择,Fragment1和Fragment2,这时需要通过从SharePreference中获取一个值来判断加载哪个Fragment,这种任务(从SharePreference中获取一个值)直接涉及到主UI的加载,只能在UI线程中执行,属于必要任务,所以应该在启动时做。
但是,如果要在Fragment加载完成后,根据SharePreference中获取的另一个值来判断是否加载一个Dialog,此时因为主UI已经加载完成,是否加载该Dialog已经不是必要任务,因此该任务(SharePreference中获取的另一个值)可以放在子线程中执行。
布局优化
布局优化是个老生常谈的问题了,因为启动时间主要来自于加载UI和加载数据,加载UI中使用LayoutInflater加载xml文件是耗时大户,所以布局优化能有效降低启动时间。常用的方法包括:使用include、merge、viewstub标签,对LayoutInflater加载的view进行复用,开启android的overDraw选项来降低xml的层级。ListView的优化
这也是个老问题了,常用的方案如复用convertView,ListView滑动时不加载图片。在实际开发中ListView会带上下布局,而使用哪种方案解决这个需求极大地影响ListView的加载性能,针对这个问题在另一篇文档中单独做了阐述(带布局的ListView),这里不再扩展。数据缓存
加载数据本身就是个耗时操作,通常也是在子线程中执行。数据的来源无非本地数据和网络数据,第一次加载只能采用网络数据,优化空间有限。从第二次加载开始,先加载本地数据,即缓存,然后更新UI,再从网络加载数据,更新UI,并更新本地缓存。加载本地数据的速度通常快于加载网络数据,因此这也是改进启动速度的有效手段。至于本地数据采用何种形式,由开发者自己决定就好。ViewPager+Fragment懒加载
在使用ViewPager+Fragment时,一般会一次性把所有Fragment都new出来,然后加载到ViewPager中。但是ViewPager默认只加载2个Fragment,即执行2个Fragment的onCreateView方法。
为了加快启动速度,我们希望只加载首个Fragment,加载完首个Fragment再去加载其他Fragment,这样会大幅提高启动速度,这种方法就是懒加载,其实质无非是通过控制加载顺序来改善启动速度。在Fragment实现懒加载的方法是,实现Fragment中的一个方法–setUserVisibleHint,该方法先于onCreateView执行,在里面判断isVisibleToUser,为true时才去加载数据。这样Fragment中便只会执行onCreateView中加载UI的方法,而不会再加载数据再更新UI,从而减少了启动时间。数据预加载
数据预加载就是要在启动时加载数据先于加载UI,加载数据在子线程,加载UI在UI线程。这里以使用EventBus作为通信方式为例,在UI加载完成后注册EventBus(防止数据加载速度快于UI加载速度,导致在更新UI时空指针)。然后,数据加载完成后用EventBus发出通知,用获取到的数据更新UI。
在实际开发中,往往会将懒加载和预加载结合使用,其核心流程就是优先加载第一个页面的数据和UI,刷新完成后再通过EventBus或其他通信框架去通知其他页面加载数据和UI。不过,在第一个页面刷新完成前用户可能会进行一些操作,引发空指针异常,因此这里要做对这些操作做好保护,防止空指针异常。