Android應用開發:LoaderManager在Activity/Fragment中的使用分析

LoaderManager

外部接口initLoader:起始

  1. public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
  2. if (mCreatingLoader) {
  3. throw new IllegalStateException("Called while creating a loader");
  4. }
  5. LoaderInfo info = mLoaders.get(id);
  6. if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
  7. if (info == null) {
  8. // Loader doesn't already exist; create.
  9. info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
  10. if (DEBUG) Log.v(TAG, " Created new loader " + info);
  11. } else {
  12. if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
  13. info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
  14. }
  15. if (info.mHaveData && mStarted) {
  16. // If the loader has already generated its data, report it now.
  17. info.callOnLoadFinished(info.mLoader, info.mData);
  18. }
  19. return (Loader<D>)info.mLoader;
  20. }
  1. mCreatingLoader代表當前LoaderManager正處於創建Loader的狀態,這個時候進入initLoader屬於衝突。
  2. LoaderInfo爲LoaderManager內部保存Loader信息的一個類,mLoaders保存了此LoaderManager加載過的Loader,需要注意:一個ID對應一個Loader。
  3. info獲取不到,說明這次是一個新的Loader進來,需要通過createAndInstallLoader進行創建和安裝,參數即爲initLoader的參數。
  4. 如果獲取到了info,說明這是一個已經存在過的Loader,只需要重新對callback回調進行重新賦值即可。
  5. 若獲取到的Loader已經開始,並且產生了有效數據,則執行LoaderInfo的callOnLoadFinished方法上報數據。
  6. 最終返回LoaderInfo中的Loader信息。

LoaderInfo聲明瞭Loader.OnLoadCompleteListener接口,並且保存了一個Loader的幾乎所有信息和狀態。

LoaderInfo的構造函數

  1. public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
  2. mId = id;
  3. mArgs = args;
  4. mCallbacks = callbacks;
  5. }

createAndInstallLoader

  1. private LoaderInfo createAndInstallLoader(int id, Bundle args,
  2. LoaderManager.LoaderCallbacks<Object> callback) {
  3. try {
  4. mCreatingLoader = true;
  5. LoaderInfo info = createLoader(id, args, callback);
  6. installLoader(info);
  7. return info;
  8. } finally {
  9. mCreatingLoader = false;
  10. }
  11. }

createLoader

  1. private LoaderInfo createLoader(int id, Bundle args,
  2. LoaderManager.LoaderCallbacks<Object> callback) {
  3. LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
  4. Loader<Object> loader = callback.onCreateLoader(id, args);
  5. info.mLoader = (Loader<Object>)loader;
  6. return info;
  7. }

通過調用callback的onCreateLoader接口創建Loader。這樣一個描述Loader的LoaderInfo就被成功創建了。完成了創建後,接下來是安裝。

installLoader

  1. void installLoader(LoaderInfo info) {
  2. mLoaders.put(info.mId, info);
  3. if (mStarted) {
  4. // The activity will start all existing loaders in it's onStart(),
  5. // so only start them here if we're past that point of the activitiy's
  6. // life cycle
  7. info.start();
  8. }
  9. }
  1. 將新的Loader裝進mLoaders保存,凡是通過此LoaderManager管理過的Loader都會有記錄的
  2. 即使是在Fragment中使用LoaderManager,其獲取方式也是通過Fragment附屬的Activity獲取,而mStarted狀態量與Activity的生命週期onStart/onStop有關。稍後做詳細分析。
  3. 若Activity的生命週期處於onStart和onStop中,則開啓Loader。

LoaderInfo的start()

  1. void start() {
  2. if (mRetaining && mRetainingStarted) {
  3. // Our owner is started, but we were being retained from a
  4. // previous instance in the started state... so there is really
  5. // nothing to do here, since the loaders are still started.
  6. mStarted = true;
  7. return;
  8. }
  9. if (mStarted) {
  10. // If loader already started, don't restart.
  11. return;
  12. }
  13. mStarted = true;
  14. if (DEBUG) Log.v(TAG, " Starting: " + this);
  15. if (mLoader == null && mCallbacks != null) {
  16. mLoader = mCallbacks.onCreateLoader(mId, mArgs);
  17. }
  18. if (mLoader != null) {
  19. if (mLoader.getClass().isMemberClass()
  20. && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
  21. throw new IllegalArgumentException(
  22. "Object returned from onCreateLoader must not be a non-static inner member class: "
  23. + mLoader);
  24. }
  25. if (!mListenerRegistered) {
  26. mLoader.registerListener(mId, this);
  27. mListenerRegistered = true;
  28. }
  29. mLoader.startLoading();
  30. }
  31. }
  1. 如果Loader還處於上一個的開始狀態中,就不做任何事情
  2. 如果已經開始了,不要重啓
  3. 若Loader爲null,則調用callback的接口再創建一個
  4. 註冊Loader的監聽器,以id爲唯一標識
  5. 啓動Loader

Loader的startLoading()

  1. public final void startLoading() {
  2. mStarted = true;
  3. mReset = false;
  4. mAbandoned = false;
  5. onStartLoading();
  6. }

會啓動Loader的onStartLoading,如果是複寫的Loader或者AsyncTaskLoader,就是執行到了這裏,是Loader在構造後執行的第一道工序。

關於結果數據的Loader回調

在LoaderInfo中給Loader註冊了回調,回調就是LoaderInfo本身。下邊以AsyncTaskLoader爲例進行分析。

AsyncTaskLoader繼承於Loader,同時其內部完成了一個AsyncTask。AsyncTaskLoader的抽象接口

  1. public abstract D loadInBackground();

其實就是運行在其內部的AsyncTask中的doInBackground。

PS: 繼承於AsyncTaskLoader的Loader不會在構造後自動啓動,需要覆寫onStartLoading中執行forceLoad,此Loader纔會在onStartLoading的生命週期時正常啓動。

在其內部的AsyncTask完成後會在onPostExecute中調用AsyncTaskLoader的dispatchOnLoadComplete

  1. void dispatchOnLoadComplete(LoadTask task, D data) {
  2. if (mTask != task) {
  3. if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel");
  4. dispatchOnCancelled(task, data);
  5. } else {
  6. if (isAbandoned()) {
  7. // This cursor has been abandoned; just cancel the new data.
  8. onCanceled(data);
  9. } else {
  10. commitContentChanged();
  11. mLastLoadCompleteTime = SystemClock.uptimeMillis();
  12. mTask = null;
  13. if (DEBUG) Slog.v(TAG, "Delivering result");
  14. deliverResult(data);
  15. }
  16. }
  17. }
  • 如果完成的是舊任務,則取消掉
  • isAbandoned是Loader的方法,通過主動調用Loader的abandon()方法可以放棄這個Loader的執行結果,如果此Loader已經被放棄,那麼調用

    1. public void onCanceled(D data)

    處理被去掉的數據,AsyncTaskLoader中沒有做任何處理,留給繼承者自行決定是否處理

  • 排除以上兩種異常情況,正常時會調用Loader的deliverResult處理數據結果。

    1. public void deliverResult(D data) {
    2. if (mListener != null) {
    3. mListener.onLoadComplete(this, data);
    4. }
    5. }

之前分析過的LoaderInfo爲自己的Loader註冊了監聽器,實現是自己,這下子數據就傳遞回去了:

  1. @Override public void onLoadComplete(Loader<Object> loader, Object data) {
  2. if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
  3. if (mDestroyed) {
  4. if (DEBUG) Log.v(TAG, " Ignoring load complete -- destroyed");
  5. return;
  6. }
  7. if (mLoaders.get(mId) != this) {
  8. // This data is not coming from the current active loader.
  9. // We don't care about it.
  10. if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active");
  11. return;
  12. }
  13. LoaderInfo pending = mPendingLoader;
  14. if (pending != null) {
  15. // There is a new request pending and we were just
  16. // waiting for the old one to complete before starting
  17. // it. So now it is time, switch over to the new loader.
  18. if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending);
  19. mPendingLoader = null;
  20. mLoaders.put(mId, null);
  21. destroy();
  22. installLoader(pending);
  23. return;
  24. }
  25. // Notify of the new data so the app can switch out the old data before
  26. // we try to destroy it.
  27. if (mData != data || !mHaveData) {
  28. mData = data;
  29. mHaveData = true;
  30. if (mStarted) {
  31. callOnLoadFinished(loader, data);
  32. }
  33. }
  34. //if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this);
  35. // We have now given the application the new loader with its
  36. // loaded data, so it should have stopped using the previous
  37. // loader. If there is a previous loader on the inactive list,
  38. // clean it up.
  39. LoaderInfo info = mInactiveLoaders.get(mId);
  40. if (info != null && info != this) {
  41. info.mDeliveredData = false;
  42. info.destroy();
  43. mInactiveLoaders.remove(mId);
  44. }
  45. if (mActivity != null && !hasRunningLoaders()) {
  46. mActivity.mFragments.startPendingDeferredFragments();
  47. }
  48. }
  1. 若LoaderInfo已經通過destroy()進行過銷燬,則不處理任何事情
  2. 若回傳的Loader與LoaderInfo描述的並不是一個,也不做任何處理
  3. mPendingLoader在LoaderManager的restartLoader中被賦值。若之前LoaderInfo中的Loader已經開始了,那麼會新建一個Loader給LoaderInfo作爲等待,在這裏,如果有等待着的Loader則直接廢棄之前的Loader,把等待的Loader扶正。同樣的,返回的數據也就不進行任何處理了。
  4. 如果是新數據,且Loader生命週期正常,則調用callOnLoadFinished對數據進行處理,這裏調用了回調函數callback中的onLoadFinished
  5. mInactiveLoaders用來保存不活躍的Loader,如果Loader不活躍了,LoaderManager並不會馬上刪除它,而是將其保存在mInactiveLoaders中,直到此Loader的新數據來了,就將其銷燬掉。

LoaderManager與生命週期

Activity和Fragment都擁有getLoaderManager的方法,其實Fragment的getLoaderManager去獲取的就是Activity所管理的衆多LoaderManager之一。

Who標籤

先來看一下Activity的getLoaderManager方法:

  1. LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
  2. if (mAllLoaderManagers == null) {
  3. mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
  4. }
  5. LoaderManagerImpl lm = mAllLoaderManagers.get(who);
  6. if (lm == null) {
  7. if (create) {
  8. lm = new LoaderManagerImpl(who, this, started);
  9. mAllLoaderManagers.put(who, lm);
  10. }
  11. } else {
  12. lm.updateActivity(this);
  13. }
  14. return lm;
  15. }

mAllLoaderManagers保存着一個Activity所擁有的所有LoaderManager,其key爲String類型的who變量。若從Activity調用getLoaderManager,那麼所得LoaderManager的who標籤爲(root):

  1. public LoaderManager getLoaderManager() {
  2. if (mLoaderManager != null) {
  3. return mLoaderManager;
  4. }
  5. mCheckedForLoaderManager = true;
  6. mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
  7. return mLoaderManager;
  8. }

若從Fragment中使用getLoaderManager,則所得LoaderManager的who標籤會根據Fragment的層級不同而不同,在Activity中處於最頂級的Fragment的who標籤爲:

  1. android:fragment:X

X爲序號。

而屬於Fragment的Fragment所活的的LoaderManager who標籤就成爲了:

  1. android:fragment:X:Y

甚至更大的深度。

LoaderManager狀態量mLoadersStarted

在開篇分析的時候分析過LoaderManager內部保存了一個mStarted狀態,很多操作會根據這個狀態做不同處理。通過上邊的分析也能看出,Fragment中獲取LoaderManager最終是通過Activity獲取的,在LoaderManager構造時,就將一個狀態量mLoadersStarted傳遞了進去,這個狀態量交給LoaderManager自行管理。

而無論是Fragment還是Activity,都有mLoadersStarted這樣一個狀態量,在onStart生命週期後爲true,onStop後爲false。所以在onStart生命週期後做initLoader操作就會使Loader一經初始化就開始運行了。

Fragment和Activity的生命週期

Fragment和Activity能夠對LoaderManager產生影響的生命週期是一樣的。

onStart

系統在onStart階段會獲取LoaderManager,如果成功獲取,則調用LoaderManager的doStart(),這裏需要特別說明的是,雖然所有LoaderManager都是保存在Activity中,但是在Activity的onStart生命週期其也只是會獲取屬於自己的(root)標籤LoaderManager,而並不是將所有保存在mAllLoaderManagers裏的Manager全部遍歷一遍。

onStop(performStop)

處於onStop生命週期,但是系統內部是通過performStop調用的。在這裏,同樣會獲取屬於自己的LoaderManager,如果Activity是因爲配置改變出發的onStop(旋轉屏幕),則調用LoaderManager的doRetain()接口,如果不是,就調用LoaderManager的doStop()接口。

onDestroy(performDestroy)

調用LoaderManager的doDestroy()接口銷燬LoaderManager。

LoaderManager的生命週期

因爲LoaderManager與Fragment/Activity的生命週期緊密相連,所以想要用好LoaderManager就必須瞭解其自身的生命週期,這樣就能把握數據的完整變化規律了。

正常的從出生到銷燬:

  1. doStart() -> doReportStart() -> doStop() -> doDestroy()

Activity配置發生變化:

  1. doStart() -> doRetain() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()

Fragment在onDestroyView()之後還會執行LoaderManager的doReportNextStart(), 即:

  1. doStart() -> doRetain() -> doReportNextStart() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()
  1. doStart()會將LoaderManager中保存的所有Loader都啓動。最終是運行每一個Loader的onStartLoading()方法。只要是通過initLoader使用過的Loader都會記錄在LoaderManager的mLoaders中,那麼問題來了:

    怎樣在Fragment/Activity不銷燬的前提下從LoaderManager中移除某個使用過的Loader呢?

    答案就是使用LoaderManager的接口去除指定ID的Loader:

    1. public void destroyLoader(int id)

    這樣就能在mLoaders中移除掉了,下次onStart的時候就沒有這個Loader什麼事了。

  2. doReportStart()。如果Fragment上一次在銷燬並重做,而且數據有效的話會在這裏主動上報數據,最終走到callback的onLoadFinished中。
  3. doStop()會停止mLoaders保存的所有Loader。最終是運行每一個Loader的onStopLoading()方法。
  4. doDestroy()會清空所有有效和無效Loader,LoaderManager中不再存在任何Loader。
  5. doRetain()會將LoaderManager的mRetaining狀態置位true,並且保存retain時LoaderInfo的mStarted狀態
  6. finishRetain()如果之前所保存的mStarted與現在的不一樣而且新的狀態是停止的話,就停止掉這個Loader。否則若有數據並且不是要下次再上報(沒有call doReportNextStart)的話就上報給callback的onLoadFinished
  7. doReportNextStart(),根據第6條,已經能夠理解了。當Fragment執行到onDestroyView生命週期時,對自己的LoaderManager發出請求:即使現在有數據也不要進行上報,等我重做再到onStart生命週期時再給我

外部接口:restartLoader

在熟知了LoaderManager的梗概之後最後分析restartLoader就能夠更好理解了:

  1. public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback)
  1. 若此ID對應的Loader之前使用過,則去除這個Loader,如果這個Loader不在運行中,則銷燬它然後重新創建一個Loader;如果在運行中,則創建一個Loader等待在這個Loader後邊
  2. 若此ID的Loader是新的,之前並沒有使用過,則創建這個Loader,同initLoader。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章