閱讀本文前,請閱讀上一篇文章《LoaderManager&Loader源碼剖析(1) – Activity對LoaderManager的管理》。
我們在使用LoaderManager時,通常做的第一件事就是:
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
是以我們先從getLoaderManager()開始吧。根據上篇的文章介紹,我們知道,最終會調用到:
class LoaderManagerImpl extends LoaderManager {
……
final String mWho;
Activity mActivity;
boolean mStarted;
LoaderManagerImpl(String who, Activity activity, boolean started) {
mWho = who;
mActivity = activity;
mStarted = started;
}
……
mWho:表明LoaderManager的身份,在這裡傳入的參數值是”(root)”。
mActivity: Activity引用。屬于哪個Activity。
mStarted:是否啟動。這個值由Activity負責傳遞的。從上篇文章介紹我們知道:
public LoaderManager getLoaderManager() {
if (mLoaderManager != null) {
return mLoaderManager;
}
mCheckedForLoaderManager = true;
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
return mLoaderManager;
}
LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
if (mAllLoaderManagers == null) {
mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
}
LoaderManagerImpl lm = mAllLoaderManagers.get(who);
if (lm == null) {
if (create) {
lm = new LoaderManagerImpl(who, this, started);
mAllLoaderManagers.put(who, lm);
}
} else {
lm.updateActivity(this);
}
return lm;
}
LoaderManagerImpl的第3個參數started實際上是由如下代碼中的mLoadersStarted傳遞的:
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
mLoadersStarted而在Activity中的onStart()指派為true的。這恰恰也說明了mLoadersStarted有一個重要作用,那就是表明Activity實際上已經進入started狀态,此時應該啟動所有的Loader了。
Activity建立完LoaderManager以後,我們通常會接着調用LoaderManager. initLoader,那麼我們接着來看一下initLoader方法:
@SuppressWarnings("unchecked")
public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}
LoaderInfo info = mLoaders.get(id);
if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
if (info == null) {
// Loader doesn't already exist; create.
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
if (DEBUG) Log.v(TAG, " Created new loader " + info);
} else {
if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}
return (Loader<D>)info.mLoader;
}
mCreatingLoader是一個保護型變量,避免在callback中重複循環調用initLoader方法。我們第一次建立id值的Loader時,需要調用createAndInstallLoader建立一個LoaderInfo執行個體來代表ID為id的Loader,否則隻是簡單更新一下mCallbacks引用。
至于createAndInstallLoader,請看代碼:
private LoaderInfo createAndInstallLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
try {
……
LoaderInfo info = createLoader(id, args, callback);
installLoader(info);
……
} finally {
……
}
}
上面做了兩件事:1)createLoader:建立一個Loader,當然代表這個Loader執行個體的是一個内部類LoaderInfo。2)installLoader:安裝這個Loader。
接着看createLoader():
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Loader<Object> loader = callback.onCreateLoader(id, args);
info.mLoader = (Loader<Object>)loader;
return info;
}
做了2件事:
1) 建立一個LoaderInfo, Loader id,參數args,還有callback都儲存了起來;
2) 調用callback的onCreateLoader,将使用者建立的Loader執行個體引用儲存到info.mLoader成員中;
再看installLoader:
void installLoader(LoaderInfo info) {
mLoaders.put(info.mId, info);
if (mStarted) {
// The activity will start all existing loaders in it's onStart(),
// so only start them here if we're past that point of the activitiy's
// life cycle
info.start();
}
}
原來調用了剛才建立的LoaderInfo的start方法來啟動loader啊:
void start() {
……
if (!mListenerRegistered) {
mLoader.registerListener(mId, this);
mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
mLoader.startLoading();
}
}
上面做了2件事:
1) 注冊監聽接口:registerListener注冊了接口onLoadComplete(),registerOnLoadCanceledListener注冊了接口onLoadCanceled(),這兩個方法有什麼用呢?我們暫不去分析源碼,否則縱深分析,最後必定毫無頭緒,目前隻分析LoaderManager吧。那麼我們先看看Loader的SDK描述即可:
public void registerListener (int id, OnLoadCompleteListener<D> listener)
Added in API level 11
注冊一個接口類,當加載完成時,接收回調。這個回調發生在主線程中,是以直接将結果傳遞給視圖元件是安全的。
必須在主線程中調用。
publicvoid registerOnLoadCanceledListener (OnLoadCanceledListener<D> listener)
Added in API level 16
注冊一個接口類,當加載取消時,接收回調。這個回調發生在主線程中,是以直接将結果傳遞給視圖元件是安全的。
必須在主線程中調用。
參數
listener | 注冊的監聽類. |
2) mLoader.startLoading:啟動Loader,開始背景資料的加載。我們看看SDK是怎麼解釋這個方法調用的:
public final void startLoading ()
Added in API level 11
當關聯的fragment/activity啟動時,這個函數會被LoaderManager自動調用。如果你使用了LoaderManager來管理Loader的話,請千萬不要自己直接調用這個方法,否則的話,你會破壞Loader的管理。開始異步加載Loader資料。當結果資料準備好的時候,會在主線程中調用相關回調。如果之前的加載已經完成,并且結果資料依然有效的話,那麼結果會被直接傳給回調方法。這個Loader會監聽資料源的變化,如果資料源發生變化,它會調用回調來通知變化。調用 stopLoading() 會停止回調傳送。
本函數會首先更新Loader的内部狀态,這樣函數isStarted()和isReset()就能夠傳回正确的狀态值。接着會調用onStartLoading()實作。
必須在主線程中調用。
再看LoaderManager上面的initLoader方法:
if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}
如果目前的Loader已經加載完資料了,并且已經是開始狀态,那麼會調用callOnLoadFinished(),其中調用了使用者的回調onLoadFinished()回調方法,通知使用者加載完成。
當然了,第一次啟動Loader,資料肯定還未加載,那麼我們在Loader加載完成後,會收到回調LoaderInfo類的 onLoadComplete方法,這個方法是在上面的LoaderInfo.start()方法中注冊的。那麼onLoadComplete做了哪些事情呢:
@Override
public void onLoadComplete(Loader<Object> loader, Object data) {
……
LoaderInfo pending = mPendingLoader;
if (pending != null) {
//(1)我們在啟動新的Loader的時候,可能一些舊的Loader正在加載,此時我們等待它們完成,然後直接銷毀即可,然後再啟動新的Loader.
if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending);
mPendingLoader = null;
mLoaders.put(mId, null);
destroy();
installLoader(pending);
return;
}
// (2)通知使用者加載完成,并把新資料傳遞給使用者,如果你啟動了多個loader,可以使用loader中的getId()方法來獲得ID進行區分。當然了,傳遞的判斷條件是兩個,第1就是新資料與舊資料不同,或者第2還未傳遞過資料,即mHaveData = false。
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
//if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this);
// (3)如果你又調用了LoaderManager.restartLoader()的話,那麼那些已經加載完成的相同ID的Loader就會進入不活動狀态,此時也需要調用它們的destory()進行銷毀。
LoaderInfo info = mInactiveLoaders.get(mId);
if (info != null && info != this) {
info.mDeliveredData = false;
info.destroy();
mInactiveLoaders.remove(mId);
}
……
}
請看上面代碼中标注的(1),(2),(3),這些都是某個Loader加載完畢時需要做的3件事。請注意它們的判斷條件。其中涉及另外兩個LoaderInfo中的方法callOnLoadFinished()和destroy()。一個是通知使用者新資料的加載完成,一個是銷毀舊的或無用的資料。
我們先來看看callOnLoadFinished(),代碼如下:
void callOnLoadFinished(Loader<Object> loader, Object data) {
…
//通知使用者回調,傳遞加載的新資料
mCallbacks.onLoadFinished(loader, data);
…
}
再看一下LoaderInfo.destroy():
void destroy() {
……
try {
//(1)通知使用者回調,Loader被重置了,此時mLoader相關的資料都不可用了,應盡快解綁定此Loader。
mCallbacks.onLoaderReset(mLoader);
} finally {
if (mActivity != null) {
mActivity.mFragments.mNoTransactionsBecause = lastBecause;
}
}
}
……
//(2)通知調用Loader的reset()方法,重置Loader。
mLoader.reset();
……
if (mPendingLoader != null) {
mPendingLoader.destroy();
}
}
上面主要做了2件事,1是通知使用者解除綁定舊Loader,一個是通知Loader重置資料,比如CursorLoader就會關閉遊标。
Loader的reset()方法我們暫不做研究。但是可以檢視一下SDK描述:
public void reset ()
Added in API level 11
此方法會更新Loader内部狀态,這樣它的isStarted()和isReset()方法就能傳回正确的值。接下來會調用Loader實作類的onReset()方法。
此方法必須在UI主線程中調用。
原來reset會先設定狀态,然後會調用Loader實作類的onReset()方法。比如CursorLoader的onReset()此時就會被調用,或者你自定義Loader的onReset()方法。
通常情況下,使用者經常會調用LoaderManager.restartLoader()方法,比如根據使用者輸入的文本進行某種資料的查找,如果使用者的查找文本發生變化的話,那麼使用者就會調用restartLoader()來進行重新查找的。
public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
……
//(1)我們首先需要先看一看是否已經建立過此id的Loader。
LoaderInfo info = mLoaders.get(id);
if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
//檢視是否有同id的舊Loader存在
if (inactive != null) {
if (info.mHaveData) {
// This loader now has data... we are probably being
// called from within onLoadComplete, where we haven't
// yet destroyed the last inactive loader. So just do
// that now.
if (DEBUG) Log.v(TAG, " Removing last inactive loader: " + info);
//(2)銷毀那些舊Loader。
inactive.mDeliveredData = false;
inactive.destroy();
//舍棄目前使用的Loader,這樣此Loader就不會再向使用者傳送資料了。
//因為此時重新開機了該Loader,舊資料無需傳遞。
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
} else {
// We already have an inactive loader for this ID that we are
// waiting for! What to do, what to do...
if (!info.mStarted) {
//(3)如果此id的Loader尚未啟動,那麼隻需銷毀即可。
if (DEBUG) Log.v(TAG, " Current loader is stopped; replacing");
mLoaders.put(id, null);
info.destroy();
} else {
// (4)此時,說明一個同id的Loader正在加載資料,但是尚未加載完成,此時我們調用它的cancel()通知退出加載。
if (DEBUG) Log.v(TAG, " Current loader is running; attempting to cancel");
info.cancel();
if (info.mPendingLoader != null) {
if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader);
info.mPendingLoader.destroy();
info.mPendingLoader = null;
}
if (DEBUG) Log.v(TAG, " Enqueuing as new pending loader");
//建立一個此id值的Loader,并賦給mPendingLoader,也許你會問為什麼不直接啟動它呢,
//别忘了,此時已經有一個Loader在加載資料了,而且我們已經調用它的cancel來通知它退出加載,當然,通常情況下,
//這個Loader也許剛好加載完成,那麼會收到onLoadComplete()回調,也許正常退出加載,此時收到回調onLoadCanceled(),
//是以在這兩個回調裡,我們再啟動mPendingLoader,不信的話,參考一下這兩個方法,就能找到installLoader(pending)。
info.mPendingLoader = createLoader(id, args,
(LoaderManager.LoaderCallbacks<Object>)callback);
return (Loader<D>)info.mPendingLoader.mLoader;
}
}
} else {
// (5)通知Loader的舍棄資料,因為此時資料應該無效了。另外别忘了儲存這個舊Loader,這樣當新資料到來的時候,就可以銷毀舊資料了。
if (DEBUG) Log.v(TAG, " Making last loader inactive: " + info);
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
//(6)如果目前還未建立過Loader,那麼就是簡單地建立并啟動一個id值的Loader即可。
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
return (Loader<D>)info.mLoader;
}
上面總共做了6件事。我們看看LoaderInfo的cancel()方法做了什麼事情呢。
void cancel() {
if (DEBUG) Log.v(TAG, " Canceling: " + this);
if (mStarted && mLoader != null && mListenerRegistered) {
//調用Loader.cancelLoad()來停止資料加載。傳回值為false時,說明此Loader已經加載完資料了,或者不能立即終止,那麼我們直接調用onLoadCanceled()方法。
if (!mLoader.cancelLoad()) {
onLoadCanceled(mLoader);
}
}
}
另外我們還需要看一下Loader的abandon()做了什麼事情:
public void abandon ()
Added in API level 11
在reset()方法之前調用,通知Loader儲存目前狀态,并且不再傳遞任何新資料給使用者。