LoaderManager 源码分析

文中提及的 Loader 相关知识基于 supprot.v4 24.0.0 中的 Loader 相关源码,android framework 的基本大同小异。

简介

关于 LoaderManager,Android Developer 上写道:LoaderManager 是与 Activity 或 Fragment 相关联的抽象类,用于管理一个或多个 Loader 实例。LoaderManager 有助于管理与 Activity 或 Fragment 生命周期相关联的、运行时间较长的操作。最常见的用法是与 CursorLoader 一起使用,但应用可以使用自定义的 Loader 用于加载其他类型的数据。

那么 LoaderManager 到底是如何管理 Loader,又是做到与 Activity、Fragment 的生命周期关联呢?本文将围绕这两个问题进行探究。

LoaderManager 与 LoaderManagerImpl

LoaderManager 实际是一个抽象类

LoaderManager_Structure

真正实现了 LoaderManager 的是 LoaderManagerImpl,FragmentActivity 中通过 getSupportLoaderManager() 和 Fragment 中 getLoaderManager() 返回的都是 LoaderManagerImpl 实例。所以,分析 LoaderManager 的机制离不开分析 LoaderManagerImpl。

源码分析

LoaderManager 的初始化

在 support.v4 中,LoaderManager 是通过 FragmentHostCallback 创建实例。

当我们调用 FragmentActivity.getSupportLoaderManager() 时,执行的代码如下:

FragmentController.java
1
2
3
4
5
private final FragmentHostCallback<?> mHost;
public LoaderManager getSupportLoaderManager() {
return mHost.getLoaderManagerImpl();
}

其中 mHost 是初始化 FragmentController 时传进来的 FragmentHostCallback 的子类 HostCallbacks,对应的 getLoaderManagerImpl 方法代码为:

FragmentHostCallback.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
LoaderManagerImpl getLoaderManagerImpl() {
if (mLoaderManager != null) {
return mLoaderManager;
}
mCheckedForLoaderManager = true;
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
return mLoaderManager;
}
// (root) 为 activity 的 LoaderManager 标识
// Fragment 的标识则为 Fragment 中的 mWho 字段
LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
if (mAllLoaderManagers == null) {
mAllLoaderManagers = new SimpleArrayMap<String, LoaderManager>();
}
LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
if (lm == null) {
if (create) {
lm = new LoaderManagerImpl(who, this, started);
mAllLoaderManagers.put(who, lm);
}
} else {
lm.updateHostController(this);
}
return lm;
}

getLoaderManager(String,boolean,boolean) 方法负责实例化 LoaderManagerImpl 并且将它储存在 SimpleArrayMap 中,方便之后复用。

Fragment.getLoaderManager 也一样是通过 FragmentHostCallback 的 mHost.getLoaderManager 获取 LoaderManagerImpl 实例。

1
2
3
4
5
6
7
8
9
10
11
public LoaderManager getLoaderManager() {
if (mLoaderManager != null) {
return mLoaderManager;
}
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mCheckedForLoaderManager = true;
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true);
return mLoaderManager;
}

至此,则完成了 LoaderManagerImpl 的初始化工作。

Loader 的创建与启动

LoaderManager 中,每一个 Loader 都会有自己的 id,开发者可以后续通过该 id 获取 Loader 实例或者进行重启、销毁操作。

LoaderManagerImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

当对应 id 的 LoaderInfo 存在且数据加载已经完成时,会将对应的 Loader 以及数据会传递到 onLoadFinished 方法中。一般来说,两个加载不同数据的 Loader 不会使用相同的 id。若使用了相同的 id,需要注意的是必须先执行 LoaderManager.destroyLoader(int) 方法将前一个同 id 的 Loader 销毁掉,否则会发生异常。

举个例子,LoaderA 需要的数据对应的 class 为 A,LoaderB 则需要 B。当 LoaderA、LoaderB 的 id 都是 1 并且 LoaderA 先加载完成。若不调用 LoaderManager.destroyLoader(int),LoaderManager 会立即返回 A 到 LoaderB 对应的回调中,此时由于 LoaderB 需要的是 B,则会立即抛出 ClassCastException。

最好的做法是使用唯一的 id,这样就不需要关心太多这些细节。

当对应 id 的 LoaderInfo 不存在时,就会调用 createAndInstallLoader 创建 LoaderInfo 实例

LoaderManagerImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
private LoaderInfo createAndInstallLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
try {
mCreatingLoader = true;
LoaderInfo info = createLoader(id, args, callback);
installLoader(info);
return info;
} finally {
mCreatingLoader = false;
}
}
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
LoaderInfo info = new LoaderInfo(id, args, callback);
Loader<Object> loader = callback.onCreateLoader(id, args);
info.mLoader = loader;
return info;
}
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();
}
}
final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
Loader.OnLoadCanceledListener<Object> {
...
void start() {
if (mRetaining && mRetainingStarted) {
// Our owner is started, but we were being retained from a
// previous instance in the started state... so there is really
// nothing to do here, since the loaders are still started.
mStarted = true;
return;
}
if (mStarted) {
// If loader already started, don't restart.
return;
}
mStarted = true;
if (DEBUG) Log.v(TAG, " Starting: " + this);
if (mLoader == null && mCallbacks != null) {
mLoader = mCallbacks.onCreateLoader(mId, mArgs);
}
if (mLoader != null) {
if (mLoader.getClass().isMemberClass()
&& !Modifier.isStatic(mLoader.getClass().getModifiers())) {
throw new IllegalArgumentException(
"Object returned from onCreateLoader must not be a non-static inner member class: "
+ mLoader);
}
if (!mListenerRegistered) {
// OnLoadCompleteListener
mLoader.registerListener(mId, this);
// OnLoadCanceledListener
mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
mLoader.startLoading();
}
}
}

可以看到,createAndInstallLoader 中实例化了 LoaderInfo 对象,并且调用了 initLoader 传进来的 LoaderCallbacks 的 onCreateLoader 方法,并赋值到 LoaderInfo 的 mLoader 中以供之后调用。创建完后调用 installLoader 方法将 LoaderInfo 存放到 mLoaders(SparseArray)中, 紧接着在 LoaderInfo 的 start 房中注册了 OnLoadCompleteListener 及 OnLoadCanceledListener 以监听 Loader 是否加载完成或取消加载,并调用 Loader 的 startLoading 方法开始加载数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Override
public void onLoadCanceled(Loader<Object> loader) {
if (mDestroyed) {
return;
}
if (mLoaders.get(mId) != this) {
return;
}
LoaderInfo pending = mPendingLoader;
if (pending != null) {// 存在相同 id 的 Loader
mPendingLoader = null;
mLoaders.put(mId, null);
destroy();
installLoader(pending);
}
}
@Override
public void onLoadComplete(Loader<Object> loader, Object data) {
if (mDestroyed) {
return;
}
if (mLoaders.get(mId) != this) {
return;
}
LoaderInfo pending = mPendingLoader;
if (pending != null) {// 存在相同 id 的 Loader
mPendingLoader = null;
mLoaders.put(mId, null);
destroy();
installLoader(pending);
return;
}
// 如果是新数据则通知应用
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
...
}

当 onLoadCanceled、onLoadComplete 回调执行后,会判断相同 id 下是否存在其他未处理的 Loader,若未处理则按照正常的流程启动该 Loader 并销毁当前已处理完的 LoaderInfo(执行 onLoaderReset 回调)。与 onLoadCanceled 不同的是 onLoadComplete 还会通知 Loader 已经加载完成。

至此,LoaderManager 管理 Loader 的加载过程已粗略了解。当然,加载过程中还会有其他细节等(如 Loader 的状态,可参考此链接),有兴趣的话可以自己阅读源码。

生命周期关联

LoaderManager 与 Activity 的生命周期关联主要是通过在 Activity 的 onStart、onStop、onDestroy 方法中调用 FragmentController 的 doLoaderStart、doLoaderStop、doDestroy 等方法达到关联的目的。

FragmentActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REALLY_STOPPED:
if (mStopped) {
doReallyStop(false);
}
break;
...
}
}
};
@Override
protected void onStart() {
super.onStart();
mStopped = false;
mReallyStopped = false;
mHandler.removeMessages(MSG_REALLY_STOPPED);
...
mFragments.doLoaderStart();
// NOTE: HC onStart goes here.
mFragments.dispatchStart();
mFragments.reportLoaderStart();
}
@Override
protected void onStop() {
super.onStop();
mStopped = true;
mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
mFragments.dispatchStop();
}
/**
* Pre-HC, we didn't have a way to determine whether an activity was
* being stopped for a config change or not until we saw
* onRetainNonConfigurationInstance() called after onStop(). However
* we need to know this, to know whether to retain fragments. This will
* tell us what we need to know.
*/
void onReallyStop() {
mFragments.doLoaderStop(mRetaining);
mFragments.dispatchReallyStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
doReallyStop(false);
mFragments.dispatchDestroy();
mFragments.doLoaderDestroy();
}

类似 Activity,Fragment 中也是通过 onStart、onDestroy、performReallyStop(该方法会在 Fragment 进入 STOPPED 状态时调用)来进行关联。

Fragment.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public void onStart() {
mCalled = true;
if (!mLoadersStarted) {
mLoadersStarted = true;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doStart();
}
}
}
public void onDestroy() {
mCalled = true;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doDestroy();
}
}
/**
* 由 FragmentManager 调用
*/
void performReallyStop() {
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchReallyStop();
}
mState = ACTIVITY_CREATED;
if (mLoadersStarted) {
mLoadersStarted = false;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
if (mHost.getRetainLoaders()) {
mLoaderManager.doRetain();
} else {
mLoaderManager.doStop();
}
}
}
}

最后

希望本文能帮助各位读者理解 LoaderManager 如何管理 Loader,以便使用 Loader 机制时能更得心应手。

(这篇文章断断续续地写了几个星期,算是第一次写较为深入源码的文章,有点力不从心。如果哪里有错、哪里写得不好,还望各位指出,谢谢。)