深入理解Android中Loader、AsyncTaskLoader、CursorLoader、LoaderManager

本文主要通过研究Loader及其子类的生命周期的方式来对Loader及其子类、LoaderManager的源码进行研究。

Loader是靠LoaderManager管理的,LoaderManager可以同时管理多个Loader,即LoaderManager与Loader是一对多的关系。我们是在Activity或Fragment使用Loader的,虽然Loader有很多public方法,但是我们不能直接调用Loader的这些public方法,因为这会扰乱LoaderManag对Loader的正常管理,我们应该通过LoaderManager的initLoader以及restartLoader方法间接管理Loader,并通过LoaderCallbacks的回调方法(onCreateLoader、onLoadFinished、onLoaderReset)对数据进行相应处理。

Loader本身不具备异步加载数据的能力;AsyncTaskLoader继承自Loader,通过AsyncTask可以异步加载数据;CursorLoader继承自AsyncTaskLoader,可以异步加载Cursor数据。

我们是在Activity或Fragment使用Loader的,Activity、Fragment与LoaderManagement交互类似于client-server模式,即Activity或Fragment是该client-server模型中的client端,即客户端,在本文中,我们所提到的客户端均指的是Loader的使用者,即Activity或Fragment。

概括来说,Loader有两大生命周期: active 和 inactive,即活动状态和非活动状态。这一点在LoaderManager的源码中体现的很直观: LoaderManager内部有两个数组mLoaders和mInactiveLoaders,其中mLoaders存储着所有处于active状态的Loader,mInactiveLoaders存储着所有处于inactive状态的Loader。

细分来说,Loader的active状态又分为started状态和stopped状态,即启动状态和停止状态,注意,stopped状态也是active生命周期的。Loader的inactive状态又分为abandoned状态和reseted状态,即废弃状态和重置状态,其中abandoned状态表明Loader现在已经废弃了,过段时间也会被重置,reseted状态表明该Loader完全被销毁重用了。Loader活跃度从高到低依次为 started -> stopped -> abandoned -> reseted,这也对应着Loader的四个生命周期,Loader处于started状态时最活跃,Loader处于reseted状态时完全不活跃。实际上,处于stopped状态的Loader也有可能再次转变为started状态,所以我们要稍将上面started到stopped之间单向箭头改为双向箭头,即Loader准确的生命周期应该是 started <-> stopped -> abandoned -> reseted。

以下是对Loader及其子类各生命周期中源码调用过程的分析,需要说明的是,由于AsyncLoader继承自Loader、CursorLoader继承自AsyncLoader,所以我们在分析代码时,这三个类都会涉及,因为子类可以实现或重写父类的方法,比如Loader类自身有个空的方法onStartLoading(),AsyncLoader没有重写该方法,但是CursorLoader重写了Loader中的空方法onStartLoading()从而CursorLoader在该方法中实现自己的逻辑。

需要注意的是,LoaderManager是个抽象类,Android有一个类LoaderManagerImpl实现了LoaderManager,Activity或Fragment中通过getLoaderManager()方法返回的其实是一个LoaderManagerImpl的实例,所以我们要同时研究LoaderManager和LoaderManagerImpl 。

Loader及其相关类的源码地址如下:
Loader源码
AsyncTaskLoader源码
CursorLoader源码
LoaderManager源码
LoaderManagerImpl源码

started

Loader在执行了startLoading()方法后,会进入started状态,LoaderManager会在合适的时机执行Loader.startLoading()方法,概括来说,当我们执行LoadManager.initLoader()时,如果Loader不存在,内部会执行LoadCallbacks的onCreateLoader()方法创建Loader,并让新创建的Loader执行startLoading()方法。

具体执行过程如下:
LoaderManager.initLoader() ->
LoaderManager.createAndInstallLoader() ->
LoaderManager.createLoader() ->
LoaderCallbacks.onCreateLoader() ->
得到loader之后创建LoaderInfo ->
LoaderManager.installLoader() ->
将其放入LoaderManager内部维护的mLoaders数组中 ->
LoaderInfo.start() ->
Loader处于started状态 ->
Loader.startLoading() ->

我们可以在onStartLoading()中写我们的逻辑,在started状态下,Loader应该监控数据源的变化,并将新数据发送给客户端,具体来说就是当监控到新数据后,调用Loader.deliverResult()方法,触发LoadCallbacks.onLoadFinished()回调的执行,从而客户端可以从该回调中轻松获取数据。

CursorLoader重写了Loader中的onStartLoading()方法,以下是CursorLoader在执行到onStartLoading()之后的逻辑,从中我们可以看出AsyncLoader和CursorLoader如何实现异步加载数据。

CursorLoader.onStartLoading() ->
AsyncTaskLoader.forceLoad() ->
Loader.forceLoad() ->
AsyncTaskLoader.onForceLoad() ->
AsyncTaskLoader.executePendingTask() ->
进入异步线程->
LoadTask.doInBackground() ->
AsyncTaskLoader.onLoadInBackground() ->
AsyncTaskLoader.doInBackground() ->
得到实际的数据 ->
退出异步线程,返回主线程 ->
LoadTask.onPostExecute() ->
AsyncTaskLoader.dispatchOnLoadComplete() ->
CursorLoader.deliverResult() -> Loader.deliverResult() ->
LoaderInfo.onLoadComplete() ->
LoaderInfo.callOnLoadFinished() ->
LoaderCallbacks.onLoadFinished()

stopped

Loader在执行了stopLoading()状态后,会进入stopped状态,LoaderManager会在合适的时机执行Loader.stopLoading()方法,概括来说,当Activity将要处于stopped状态的时候会执行Activity的performStop()方法,在该方法内会执行FragmentController的doLoaderStop()方法,在该方法内又会执行FragmentHostCallback的 doLoaderStop()方法,在该方法内部会执行LoaderManager的doStop()方法,在该方法内,LoaderManager会遍历所有的内部存储的LoaderInfo,依次执行LoaderInfo的stop()方法,在该方法中内部又会调用Loader.stopLoading()方法,此时Loader进入stopped方法,然后执行Loader的stopLoading()。

started -> stopped状态变化的具体执行过程如下:
Activity.performStop() ->
FragmentController.doLoaderStop() ->
FragmentHostCallback.doLoaderStop() ->
LoaderManager.doStop() ->
所有LoaderInfo.stop() ->
Loader.stopLoading() ->
Loader进入stopped状态 ->
onStopLoading(),我们可以在onStartLoading()中写我们的逻辑。

由上面的调用过程我们可以发现,当Activity处于stopped状态时,所有的Loader也会被置于stopped状态,其实你如果去观察Fragment的源码也会发现当Fragment处于stopped状态时,所有的Loader也会被置于stopped状态。

当Activity执行onStart()方法的时候,其内部又会执行FragmentController的doLoaderStart()方法,进而执行FragmentHostCallback的doLoaderStart()方法,进而执行LoaderManager的doStart()方法,内部会遍历所有LoaderInfo,并执行LoaderInfo的start()方法,进而执行Loader的startLoading()方法,从而Loader又进入了started状态,进而执行Loader的onStartLoading()。

stopped -> started状态变化的具体执行过程如下:
Activity.onStart() ->
FragmentController.doLoaderStart() ->
FragmentHostCallback.doLoaderStart() ->
LoaderManager.doStart() ->
所有LoaderInfo.start() ->
Loader.startLoading() ->
Loader从stopped状态进入started状态 ->
Loader.onStartLoading()。

由上面的调用过程我们可以发现,当Activity从stopped重新处于started状态时,所有处于stopped状态的Loader也会被从stopped状态变为started状态,其实你如果去观察Fragment的源码也会发现当Fragment从stopped状态变为started状态时,Loader也会从stopped状态变为started状态。

通过上面的描述,我们可以得知,android想保持Loader与其用者(Activity或Fragment)的状态保持一致,同为started或同为stopped。

当Loader处于stopped状态的时候,我们应该继续监听数据的变化,但是此时有新数据的时候我们应该先存起来,不将其发送给客户端,因为客户端(Activity或Fragment)也处于stopped状态,发给客户端也没啥用。如果Loader从stopped状态变为了started状态,那么我们此时应该将之前监听到的新数据发送给客户端(Activity或Fragment),因为客户端此时肯定也处于started状态,发给客户端之后,客户端就可以更新UI之类的。

abandoned

Loader在执行了abandon()方法后,会进入abandoned状态,LoaderManager会在合适的时机执行Loader.abandon()方法,概括来说,当执行LoaderManager.restartLoader(loaderId)时候,如果指定loaderId对应的Loader存在,而且处于active状态(具体包括started和stopped两种状态),那么就会调用原Loader的abandon()方法,这样原Loader就处于abandoned状态,并且LoaderManager会将该Loader放入到LoaderManager内部维护的mInactiveLoaders数组中,在原Loader进入abandoned状态后,我们会创建一个新的Loader。此时,刚刚放入到mInactiveLoaders数组中的loader就变成了一个老的废弃的loader,注意此时该老Loader还不会销毁,是因为我们虽然创建了新的loader,但是新的loader还没有即可获取到数据,所以此时我们依然还要保存该老的loader,这样客户端在没有得到新loader发来的数据前还可以继续使用老的loader的数据,一旦新loader加载完了数据并发给了客户端,那此时该老的loader就完全没用了,LoaderManager会将其置于reseted状态并将其从mInactiveLoaders中移除掉,即这个老loader被销毁了,关于如何从abandoned变成reseted的细节下面还会从代码调用上详细解释。

abandoned状态是Loader从stopped到reseted状态的一个中间状态,一般情况下,进入abandoned状态的Loader会过段时间被完全销毁标记为reseted状态,所以abandoned状态的Loader不应再监听数据源的变化,更不应该将变化的数据源发送给客户端。

reseted

Loader在执行了reset()方法后,会进入rested状态,LoaderManager会在合适的时机执行Loader.reset()方法,具体执行过程如下:
上面提到,在执行了LoaderManager的restartLoader()方法后,我们会将原始的loader放入mInactiveLoaders中使其处于abandoned转台,之后会立即创建一个新Loader。

新Loader会开始其自己新的征程,具体执行过程如下:
LoadManager.initLoader() ->
LoaderManager.createAndInstallLoader ->
LoadCallbacks.onCreateLoader() ->
LoaderManager.installLoader ->
将其放入LoaderManager内部维护的mLoaders数组中 ->
Loader.startLoading() ->
Loader进入started状态 ->
Loader.onStartLoading(),此时我们会在Loader的onStartLoading()中去加载数据,当加载完毕后,我们会执行Loader.deliverResult(data)去将数据派发出去给客户端使用,从该方法开始,其调用顺序是Loader.deliverResult(data) ->
LoaderInfo.onLoadComplete() ->
LoaderInfo.callOnLoadFinished ->
LoaderCallbacks.onLoadFinished ->
客户端拿到数据 ->
检查该loaderId在mInactiveLoaders中有没有对应loaderId的loader,如果能找到,那么就说明从mInactiveLoaders中找到的这个被废弃的loader恰恰是上面我们解释abandoned的时候由于LoaderManager.restartLoader(loaderId)的执行被塞到mInactiveLoaders并处于abandoned状态的,那么此时mInactiveLoaders中的这个老loader就完全没用了,LoaderManager会调用该废弃Loader的reset()方法,对于该废弃的Loader具体执行过程是:
废弃的LoaderInfo.destroy() ->
废弃的Loader.reset() ->
废弃的Loader.onReset() ->
废弃的Loader此时状态从abandoned变为rested状态 ->
最后LoaderManager将其从mInactiveLoaders中移除掉,即这个老loader被销毁了。

处于reseted状态的Loader被完全销毁了,或者准确的说LoaderManager把该Loader标记为销毁状态reseted。

希望本文对大家理解Loader的生命周期有所帮助,在理解了Loader生命周期的基础上,我们才能写出符合自己需求的Loader

你可能感兴趣的:(源码解析,Loader,loadermana)