天天看點

深入了解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

繼續閱讀