阅读本文前,请阅读上一篇文章《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保存当前状态,并且不再传递任何新数据给用户。