Android应用开发:LoaderManager在Activity/Fragment中的使用分析
LoaderManager
外部接口initLoader:起始
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代表当前LoaderManager正处于创建Loader的状态,这个时候进入initLoader属于冲突。
- LoaderInfo为LoaderManager内部保存Loader信息的一个类,mLoaders保存了此LoaderManager载入过的Loader,须要注意:一个ID相应一个Loader。
- info获取不到,说明这次是一个新的Loader进来,须要通过createAndInstallLoader进行创建和安装。參数即为initLoader的參数。
- 假设获取到了info,说明这是一个已经存在过的Loader,仅仅须要又一次对callback回调进行又一次赋值就可以。
- 若获取到的Loader已经開始。而且产生了有效数据。则运行LoaderInfo的callOnLoadFinished方法上报数据。
- 终于返回LoaderInfo中的Loader信息。
LoaderInfo声明了Loader.OnLoadCompleteListener接口。而且保存了一个Loader的差点儿全部信息和状态。
LoaderInfo的构造函数
public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
mId = id;
mArgs = args;
mCallbacks = callbacks;
}
createAndInstallLoader
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;
}
}
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;
}
通过调用callback的onCreateLoader接口创建Loader。这样一个描写叙述Loader的LoaderInfo就被成功创建了。
完毕了创建后,接下来是安装。
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();
}
}
- 将新的Loader装进mLoaders保存,凡是通过此LoaderManager管理过的Loader都会有记录的
- 即使是在Fragment中使用LoaderManager。其获取方式也是通过Fragment附属的Activity获取。而mStarted状态量与Activity的生命周期onStart/onStop有关。稍后做具体分析。
- 若Activity的生命周期处于onStart和onStop中,则开启Loader。
LoaderInfo的start()
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) {
mLoader.registerListener(mId, this);
mListenerRegistered = true;
}
mLoader.startLoading();
}
}
- 假设Loader还处于上一个的開始状态中。就不做不论什么事情
- 假设已经開始了,不要重新启动
- 若Loader为null,则调用callback的接口再创建一个
- 注冊Loader的监听器。以id为唯一标识
- 启动Loader
Loader的startLoading()
public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}
会启动Loader的onStartLoading。假设是复写的Loader或者AsyncTaskLoader,就是运行到了这里。是Loader在构造后运行的第一道工序。
关于结果数据的Loader回调
在LoaderInfo中给Loader注冊了回调,回调就是LoaderInfo本身。下边以AsyncTaskLoader为例进行分析。
AsyncTaskLoader继承于Loader,同一时候其内部完毕了一个AsyncTask。
AsyncTaskLoader的抽象接口
public abstract D loadInBackground();
事实上就是执行在其内部的AsyncTask中的doInBackground。
PS: 继承于AsyncTaskLoader的Loader不会在构造后自己主动启动,须要覆写
onStartLoading
中运行forceLoad
。此Loader才会在onStartLoading的生命周期时正常启动。
在其内部的AsyncTask完毕后会在onPostExecute
中调用AsyncTaskLoader的dispatchOnLoadComplete
void dispatchOnLoadComplete(LoadTask task, D data) {
if (mTask != task) {
if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel");
dispatchOnCancelled(task, data);
} else {
if (isAbandoned()) {
// This cursor has been abandoned; just cancel the new data.
onCanceled(data);
} else {
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask = null;
if (DEBUG) Slog.v(TAG, "Delivering result");
deliverResult(data);
}
}
}
- 假设完毕的是旧任务,则取消掉
isAbandoned是Loader的方法,通过主动调用Loader的
abandon()
方法能够放弃这个Loader的运行结果。假设此Loader已经被放弃。那么调用public void onCanceled(D data)
处理被去掉的数据,AsyncTaskLoader中没有做不论什么处理,留给继承者自行决定是否处理
排除以上两种异常情况。正常时会调用Loader的
deliverResult
处理数据结果。public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}
之前分析过的LoaderInfo为自己的Loader注冊了监听器。实现是自己,这下子数据就传递回去了:
@Override public void onLoadComplete(Loader<Object> loader, Object data) {
if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
if (mDestroyed) {
if (DEBUG) Log.v(TAG, " Ignoring load complete -- destroyed");
return;
}
if (mLoaders.get(mId) != this) {
// This data is not coming from the current active loader.
// We don't care about it.
if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active");
return;
}
LoaderInfo pending = mPendingLoader;
if (pending != null) {
// There is a new request pending and we were just
// waiting for the old one to complete before starting
// it. So now it is time, switch over to the new loader.
if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending);
mPendingLoader = null;
mLoaders.put(mId, null);
destroy();
installLoader(pending);
return;
}
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
//if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this);
// We have now given the application the new loader with its
// loaded data, so it should have stopped using the previous
// loader. If there is a previous loader on the inactive list,
// clean it up.
LoaderInfo info = mInactiveLoaders.get(mId);
if (info != null && info != this) {
info.mDeliveredData = false;
info.destroy();
mInactiveLoaders.remove(mId);
}
if (mActivity != null && !hasRunningLoaders()) {
mActivity.mFragments.startPendingDeferredFragments();
}
}
- 若LoaderInfo已经通过
destroy()
进行过销毁。则不处理不论什么事情 - 若回传的Loader与LoaderInfo描写叙述的并非一个,也不做不论什么处理
- mPendingLoader在LoaderManager的restartLoader中被赋值。若之前LoaderInfo中的Loader已经開始了,那么会新建一个Loader给LoaderInfo作为等待。在这里,假设有等待着的Loader则直接废弃之前的Loader,把等待的Loader扶正。
相同的。返回的数据也就不进行不论什么处理了。
- 假设是新数据。且Loader生命周期正常,则调用
callOnLoadFinished
对数据进行处理,这里调用了回调函数callback中的onLoadFinished
- mInactiveLoaders用来保存不活跃的Loader,假设Loader不活跃了。LoaderManager并不会立即删除它,而是将其保存在mInactiveLoaders中,直到此Loader的新数据来了。就将其销毁掉。
LoaderManager与生命周期
Activity和Fragment都拥有getLoaderManager
的方法。事实上Fragment的getLoaderManager
去获取的就是Activity所管理的众多LoaderManager之中的一个。
Who标签
先来看一下Activity的getLoaderManager
方法:
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;
}
mAllLoaderManagers
保存着一个Activity所拥有的全部LoaderManager,其key为String类型的who变量。若从Activity调用getLoaderManager
,那么所得LoaderManager的who标签为(root)
:
public LoaderManager getLoaderManager() {
if (mLoaderManager != null) {
return mLoaderManager;
}
mCheckedForLoaderManager = true;
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
return mLoaderManager;
}
若从Fragment中使用getLoaderManager。则所得LoaderManager的who标签会依据Fragment的层级不同而不同。在Activity中处于最顶级的Fragment的who标签为:
android:fragment:X
X为序号。
而属于Fragment的Fragment所活的的LoaderManager who标签就成为了:
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就必须了解其自身的生命周期,这样就能把握数据的完整变化规律了。
正常的从出生到销毁:
doStart() -> doReportStart() -> doStop() -> doDestroy()
Activity配置发生变化:
doStart() -> doRetain() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()
Fragment在onDestroyView()之后还会运行LoaderManager的doReportNextStart(), 即:
doStart() -> doRetain() -> doReportNextStart() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()
doStart()
会将LoaderManager中保存的全部Loader都启动。终于是执行每个Loader的
onStartLoading()
方法。仅仅要是通过initLoader使用过的Loader都会记录在LoaderManager的mLoaders中。那么问题来了:
如何在Fragment/Activity不销毁的前提下从LoaderManager中移除某个使用过的Loader呢?
答案就是使用LoaderManager的接口去除指定ID的Loader:
public void destroyLoader(int id)
这样就能在mLoaders中移除掉了。下次onStart的时候就没有这个Loader什么事了。
doReportStart()
。假设Fragment上一次在销毁并重做,并且数据有效的话会在这里主动上报数据,终于走到callback的onLoadFinished
中。doStop()
会停止mLoaders保存的全部Loader。终于是执行每个Loader的onStopLoading()
方法。doDestroy()
会清空全部有效和无效Loader。LoaderManager中不再存在不论什么Loader。doRetain()
会将LoaderManager的mRetaining状态置位true。并且保存retain时LoaderInfo的mStarted状态finishRetain()
假设之前所保存的mStarted与如今的不一样并且新的状态是停止的话。就停止掉这个Loader。否则若有数据并且不是要下次再上报(没有call
doReportNextStart)的话就上报给callback的onLoadFinished
。doReportNextStart()
。依据第6条,已经可以理解了。当Fragment运行到onDestroyView生命周期时,对自己的LoaderManager发出请求:即使如今有数据也不要进行上报。等我重做再到onStart生命周期时再给我
。
外部接口:restartLoader
在熟知了LoaderManager的梗概之后最后分析restartLoader
就行更好理解了:
public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback)
- 若此ID相应的Loader之前使用过。则去除这个Loader,假设这个Loader不在执行中。则销毁它然后又一次创建一个Loader;假设在执行中。则创建一个Loader等待在这个Loader后边
- 若此ID的Loader是新的,之前并没有使用过。则创建这个Loader,同initLoader。
Android应用开发:LoaderManager在Activity/Fragment中的使用分析的更多相关文章
- Android项目开发填坑记-Fragment的onBackPressed
Github版 CSDN版 知识背景 Fragment在当前的Android开发中,有两种引用方式,一个是 Android 3.0 时加入的,一个是supportV4包中的.这里简称为Fragment ...
- Android项目开发填坑记-Fragment的onAttach
背景 现在Android开发多使用一个Activity管理多个Fragment进行开发,不免需要两者相互传递数据,一般是给Fragment添加回调接口,让Activity继承并实现. 回调接口一般都写 ...
- 安卓开发_WebView如何在Fragment中使用
之前学习了如何在activity中使用WebView控件来显示网页. 在我的实际开发中,有需要在Fragment中用到WebView控件的,那么就百度学习了一下 其实很简单,但是当然不是和在Activ ...
- Android UI开发详解之Fragment
Fragment是Android自从3.0之后新加入的一个组件,我相信很多人都已经听说过这个组件了,但这个组件到底是个什么,如何去使用他呢,且听我讲来. 以下部分资料来自官网(官网才是王道,其他都是浮 ...
- Data binding 在Activity,Fragment中引用以及加载其他布局
Data binding在Activity中使用: DataBindingUtil.setContentView(this, R.layout.activity_home); Data binding ...
- Android Camera开发:周期性循环自动聚焦auto focus挂掉原因分析(preview is not enabled)
参考:Android Camera开发:扫描二维码,周期性循环自动聚焦auto focus挂掉原因分析(preview is not enabled) 最近做Android人脸识别时,camera在自 ...
- Android应用开发学习笔记之Fragment
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Fragment翻译成中文就是“碎片”.“片断”的意思,Fragment通常用来作为一个Activity用户界面的一 ...
- Android入门开发之销毁activity
使用: 销毁.关闭页面activity 如果打开下个页面的同时销毁了本页面,在下个页面无法返回本页面,每次打开APP应用就会先显示一张APP的介绍图.或者LOGO页面,延时几秒进入应用,进入后无法再返 ...
- Android NDK开发篇:如何使用JNI中的global reference和local reference
JNI提供了一些实例和数组类型(jobject.jclass.jstring.jarray等)作为不透明的引用供本地代码使用.本地代码永远不会直接操作引用指向的VM内部的数据内容.要进行这些操作,必须 ...
随机推荐
- [WPF]使用Pack URI路径訪问二进制资源
一.路径格式定义 完整的URI定义为: pack://application,,,[/可选程序集名称;][可选版本;][目录名称/]文件名 缩略后的写法是: [目录名称/]文件名 二.在XAML代码中 ...
- SE 2014年4月24日
如图配置交换网络 由于网络规模较小,企业将网络划分为了接入层和核心层两层 核心层设备(Sw1 Sw2 Sw3)作为用户的网关设备,提供三层转发功能 接入层设备(SW4 SW5)连接用户,分别划分三vl ...
- 一次失败的刷题经历:[LeetCode]292之尼姆游戏(Nim Game)(转)
最近闲来无事刷LeetCode,发现这道题的Accept Rate还是挺高的,尝试着做了一下,结果悲剧了,把过程写下来,希望能长点记性.该题的描述翻译成中文如下: 你正在和你的朋友玩尼姆游戏(Nim ...
- IntelliJ IDEA中怎样使用JUnit4
背景 近期參与了一个Anroid医疗项目,当中项目底层有非常多基础类及通讯类,并且非常多涉及复杂的字节操作还有多线程同步及状态机处理.这种项目做一下TDD还是必要的,尽量项眼下期把风险减少一些. ...
- SVN的revert和update命令的区别
svn中的revert和update 今天有人问到revert和update的问题. 刚开始还真被问住了. 因为感觉revert和update都可以将本地的copy更新到以前的一个版本,会有什么不同呢 ...
- 该View转换成Bitmap方法
方法一: /** * 该View绘制到Bitmap上 * @param view 须要绘制的View * @param width 该View的宽度 * @param height 该View的高度 ...
- eclipse安装ADT后在windows菜单下找不到android SDK and AVD Manager选项的解决办法
在eclipse中点击window→Customize Perspective→Command Groups availability→Available command groups下勾选Andro ...
- javamail发送邮件(转)
今天学习了一下JavaMail,javamail发送邮件确实是一个比较麻烦的问题.为了以后使用方便,自己写了段代码,打成jar包,以方便以后使用.呵呵 以下三段代码是我的全部代码,朋友们如果想用,直接 ...
- docker 现实---中小企业docker环境结构(五)
docker对于中小企业,设定paas他没有足够的能量,没有必要为,个人二手sandbox实用性和小点.我个人觉得,中小企业可以使用docker要规范发展.测试.生产环境. 他画了一个简单的图表: d ...
- Windows Phone开发(15):资源
原文:Windows Phone开发(15):资源 活字印刷术是我国"四大发明"之一,毕昇在发明活字印刷术之后,他很快发现一个问题,随着要印刷资料的不断增加,要用到的汉字数目越来越 ...