深入原始碼解析Android中Loader、AsyncTaskLoader、CursorLoader、LoaderManager

孫群發表於2015-10-08

如果對Loader、AsyncTaskLoader、CursorLoader、LoaderManager等概念不明白或不知道如何使用Loader機制,可參見博文Android中Loader及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的文章。

相關閱讀:
我的Android博文整理彙總
Android中Loader及LoaderManager的使用(附原始碼下載)
使用詳解及原始碼解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter

相關文章