本文主要从Loader入手, 去分析Loader使用、Loader的源码分析等,主要分为以下四篇:
本文基于原生的 Android8.0源码进行分析。以下是第二篇内容
Activity/Fragment对LoaderManager的管理
学会了Loader的使用后,就可以进入源码去更多的了解Loader了。
在实例代码中, 我们知道了,使用Loader, 就是从初始化Loader这么一行代码开始的:
getLoaderManager().initLoader(1, null, this);
这一行代码使用起来简单, 但是却内有乾坤, Google给我们做了很多封装,比如getLoaderManager()得到的LoaderManager对象,是什么时候创建的, 在哪里创建的?initLoader, 是如何初始化我们想要的Loader的?
我们这里从Activity中的getLoaderManager()去分析源码, Fragment的源码流程和这个类似,跟一下源码就很明白了。
一、 Activity中创建LoaderManager 对象
Activity对LoaderManager的管理主要有涉及四个生命周期方法:
- onStart()
- performStop()
- performDestory()
- performStart()
1. Activity的onStart()方法中创建LoaderManager
首先看看onStart()方法:
@CallSuper
protected void onStart() {
mCalled = true;
mFragments.doLoaderStart();
...
}
跟进mFragments.doLoaderStart(), 最终进入了FragmentHostCallback.java:
void doLoaderStart() {
if (mLoadersStarted) {
return;
}
mLoadersStarted = true;
if (mLoaderManager != null) {
mLoaderManager.doStart();
} else if (!mCheckedForLoaderManager) {
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
}
mCheckedForLoaderManager = true;
}
这里去判断,如果发现mLoaderManager不为空,说明它已经被用户通过getLoaderManager()创建了,当前用户正在使用这个LoaderManager,那么需要调用它的doStart()方法来启动它。 如果值为空,那么通过getLoaderManager(String who, boolean started, boolean create)方法来获取这个引用。这个调用其实和LoaderManager的恢复机制有关,关于恢复,后面再介绍。注意第3个参数create值为false,说明不会创建新的LoaderManager,它只是去查询获得引用。代码如下:
LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
if (mAllLoaderManagers == null) {
mAllLoaderManagers = new ArrayMap<String, LoaderManager>();
}
LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
if (lm == null && create) {
lm = new LoaderManagerImpl(who, this, started);
mAllLoaderManagers.put(who, lm);
} else if (started && lm != null && !lm.mStarted){
lm.doStart();
}
return lm;
}
最终LoaderManager的实例引用保存在了mAllLoaderManagers这个ArrayMap集合里面。
2. Activity的performStop()方法中停止LoaderManager
关键代码如下:
final void performStop(boolean preserveWindow) {
......
mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
......
}
注意到调用停止LoaderManager时,这里传入了mChangingConfigurations这个boolean类型的参数, 看一下它的声明:
/** true if the activity is being destroyed in order to recreate it with a new configuration */
/*package*/ boolean mChangingConfigurations = false;
就是说如果activity被销毁然后重建的话, 这个值就会为true, 这种情况有:切换横竖屏、修改语言等。 这个值仅在一处地方被设置为true, 就是在ActivityThread中的handleRelaunchActivity方法内:
private void handleRelaunchActivity(ActivityClientRecord tmp) {
......
r.activity.mChangingConfigurations = true;
......
}
继续跟进代码, 最终又进入FragmentHostCallback这个中间类,这个中间类的作用就是连接Activity/Fragment和LoaderManager, 从而将Activity/Fragment和LoaderManager之间的耦合降到最低,这种代码的设计就是迪米特法则的使用, 还是看代码吧:
void doLoaderStop(boolean retain) {
mRetainLoaders = retain;
if (mLoaderManager == null) {
return;
}
if (!mLoadersStarted) {
return;
}
mLoadersStarted = false;
if (retain) {
mLoaderManager.doRetain();
} else {
mLoaderManager.doStop();
}
}
这里的retain实际上就是上面我们传入的mChangingConfigurations这个boolean值, 并进行了判断, 如果是true, 及有Activity的销毁再创建的过程存在,则调用doRetain()把LoaderManager存起来, 否则直接调用doStop。这里存起来就是为了在Activity再创建时,直接恢复LoaderManager对象。
3. Activity的performDestory()
final void performDestroy() {
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
}
最终调用到LoaderManager的doDestroy()方法:
void doDestroy() {
if (!mRetaining) {
if (DEBUG) Log.v(TAG, "Destroying Active in " + this);
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).destroy();
}
mLoaders.clear();
}
if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this);
for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {
mInactiveLoaders.valueAt(i).destroy();
}
mInactiveLoaders.clear();
mHost = null;
}
在这里,进行了判断,如果不是需要恢复状态的,就清除了LoaderManager所管理的Loader。否则不会清除
4. Activity的performStart()方法
final void performStart() {
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStart()");
}
mFragments.dispatchStart();
mFragments.reportLoaderStart();
...
}
在重新创建的Activity通过mFragments.reportLoaderStart() 恢复了LoaderManager。
二、 LoaderManager的恢复
切换横竖屏或者语言后, Activity会销毁重建一次, 也会对LoaderManager进行恢复, 恢复的方法在Activity的retainNonConfigurationInstances()方法中:
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
// We're already stopped but we've been asked to retain.
// Our fragments are taken care of but we need to mark the loaders for retention.
// In order to do this correctly we need to restart the loaders first before
// handing them off to the next activity.
mFragments.doLoaderStart();
......
}
Activity对LoaderManager的管理就说完了, 下一篇将分析LoaderManager对Loader的管理。