android之LoaderManager原理分析

2024-01-05 03:48

本文主要是介绍android之LoaderManager原理分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.    LoaderManager的使用
    LoaderManager用于异步加载数据,当然,它异步的实现依赖于AsyncTask的原理。如果不深究它的实现原理,这个LoaderManager的使用还是很简单的。我们只需要实现LoaderCallbacks类,然后调用getLoaderManager().initLoader()就可以了,执行加载数据的语句写在LoaderCallbacks类的onCreateLoader()方法中,如果加载完毕,我们可以在onLoadFinished()方法中获取相关的数据结果。具体代码如下:

public class MainActivity extends Activity {private String TAG = "LoaderManager";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);getLoaderManager().initLoader(0, null, mCallback);}private final LoaderManager.LoaderCallbacks<Cursor> mCallback = new LoaderCallbacks<Cursor>() {@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {// TODO Auto-generated method stubandroid.util.Log.v(TAG, "LoaderCallbacks-------onCreateLoader");return new CursorLoader(getApplicationContext(), Telephony.MmsSms.CONTENT_CONVERSATIONS_URI, null, null, null, null);//return null;}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {// TODO Auto-generated method stubandroid.util.Log.v(TAG, "LoaderCallbacks-------onLoadFinished----count:"+data.getCount());while(data.moveToNext()){android.util.Log.v(TAG,":"+data.getString(data.getColumnIndex("address")));android.util.Log.v(TAG,":"+data.getString(data.getColumnIndex("body")));}}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {// TODO Auto-generated method stubandroid.util.Log.v(TAG, "LoaderCallbacks-------onCreateLoader");}};
}

以上代码的功能是从Mms数据库中读取所有短信信息并打印。

二.    LoaderManager源代码分析


1.LoaderManager

那么为什么要实现LoaderCallbacks类呢(回调机制),我们等会再说。先来看看怎么获取LoaderManage对象----getLoaderManager():我们可以看到,Activity.java和Fragment.java中都有这个方法,说明我们在创建每一个Activity和Fragment的时候,都会生成一个相对应的LoaderManager对象。当然,在显式的调用getLoaderManager()的时候,如果先前没有创建过LoaderManager对象,它会直接new LoaderManagerImpl()并返回这个对象。如果先前已经创建过LoaderManager对象,那么就会直接使用以前的LoaderManager对象。

public LoaderManager getLoaderManager() {if (mLoaderManager != null) {return mLoaderManager;}mCheckedForLoaderManager = true;mLoaderManager = getLoaderManager(null, mLoadersStarted, true);return mLoaderManager;}LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {if (mAllLoaderManagers == null) {mAllLoaderManagers = new HashMap<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;
}

其实在Activity.java和Fragment.java的onStart()方法中,也有初始化这个LoaderManager对象,只是它并不会创建新对象,只会使用旧的对象进行赋值,如下:
protected void onStart() {if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);mCalled = true;if (!mLoadersStarted) {mLoadersStarted = true;if (mLoaderManager != null) {mLoaderManager.doStart();} else if (!mCheckedForLoaderManager) {mLoaderManager = getLoaderManager(null, mLoadersStarted, false);}mCheckedForLoaderManager = true;}getApplication().dispatchActivityStarted(this);}

LoaderManager对象得到后,接下来会调用initLoader()方法。接下来的东西就跟LoaderManager.java有关了。先来了解一下LoaderManager.java这个类都有哪些方法和内部类。
abstract class LoaderManager:
    一个接口:interface LoaderCallbacks<D>
           方法:initLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<D> callback)
                     restartLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<D> callback)
                     destroyLoader(int id);
        内部类:class LoaderManagerImpl extends LoaderManager {…}
             内部类的内部类:final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
                                      Loader.OnLoadCanceledListener<Object> {

由于LoaderManager是一个抽象类,所以它的子类LoaderManagerImpl必须要实现它的几个抽象方法。而这几个抽象方法使用了LoaderCallbacks类型的变量作为形参,所以,我们要自己实现LoaderCallbacks这个接口。那么,initLoader和restartLoader这两个方法是干嘛用的呢?要了解这个,我们必须先得了解Loader.java这个类。


2. Loader
public class Loader<D>
     内部类:public final class ForceLoadContentObserver extends ContentObserver
     内部接口:public interface OnLoadCompleteListener<D>
               public interface OnLoadCanceledListener<D>
     方法:deliverResult(D data)
           registerListener(int id, OnLoadCompleteListener<D> listener)
           startLoading()
Loader的继承关系如下:
Loader
AsyncTaskLoader
     CursorLoader,ContactLoader……..
先来看看Loader的两个内部接口,这两个内部接口是用来干嘛的呢? 要想知道它的作用,那么先得找到是谁实现了它,又是谁调用了它。我们看到在deliverResult()方法中,接口的方法onLoadComplete被调用了。

public void deliverResult(D data) {if (mListener != null) {mListener.onLoadComplete(this, data);}
}

而这个接口的实现对象mListener是被registerListener()传进来的。

public void registerListener(int id, OnLoadCompleteListener<D> listener) {if (mListener != null) {throw new IllegalStateException("There is already a listener registered");}mListener = listener;mId = id;}
再来看看实现它的地方,是在LoaderManager的内部实现类LoaderManagerImpl中,如下:

final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,Loader.OnLoadCanceledListener<Object>

这里也使用了回调机制,如果有谁调用了deliverResult()方法,就通过回调机制通知LoaderInfo的onLoadComplete()方法,通过字眼可知,到了deliverResult被调用的时候,需要加载的数据也就得到了,当然得通知LoaderInfo方法进行数据处理,再由LoaderManager统一部署,最后也是通过回调机制回到LoaderCallbacks的实现方法onLoadFinished()中。那么Loader的作用也就出来了:
1. 检测数据源,如果有变化则通知(ForceLoadContentObserver)。
2.定义Loader的状态,根据状态的不同,执行不同的操作,状态有:mStarted,mAbandoned,mReset
3.回调机制:OnLoadCompleteListener,OnLoadCanceledListener
3. AsyncTaskLoader
关于异步的过程并不是在Loader实现,而是在它的子类AsyncTaskLoader中实现。来看看AsyncTaskLoader中的方法及内部类:

volatile LoadTask mTask;
final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable
onForceLoad():重载
在上一节有关于AsyncTask的介绍中,我们知道使用AsyncTask进行异步时,只需要创建一个AsyncTask的对象,并调用execute()方法就可以了。所以可以看看都有哪个方法执行了异步的开始方法:

@Overrideprotected void onForceLoad() {super.onForceLoad();cancelLoad();mTask = new LoadTask();if (DEBUG) Slog.v(TAG, "Preparing load: mTask=" + mTask);executePendingTask();
}void dispatchOnCancelled(LoadTask task, D data) {onCanceled(data);if (mCancellingTask == task) {if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!");mLastLoadCompleteTime = SystemClock.uptimeMillis();mCancellingTask = null;if (DEBUG) Slog.v(TAG, "Delivering cancellation");deliverCancellation();executePendingTask();}}

dispatchOnCancelled()方法不是很明确它到底要干什么,我的理解是:大概在加载数据时(LoadTask),时间比较长,等结果出来时,新的Loader任务来了(new LoadTask()),所以放弃掉旧数据,重新开始加载。有待再研究。
      毫无疑问,onForceLoad()方法创建了AsyncTask对象,并开启了异步线程的任务。任务开始后,后台的执行方法是doInBackground():详细的看上一节AsyncTask介绍。

protected D doInBackground(Void... params) {if (DEBUG) Slog.v(TAG, this + " >>> doInBackground");try {D data = AsyncTaskLoader.this.onLoadInBackground();if (DEBUG) Slog.v(TAG, this + "  <<< doInBackground");return data;} catch (OperationCanceledException ex) {if (!isCancelled()) {throw ex;}if (DEBUG) Slog.v(TAG, this + "  <<< doInBackground (was canceled)");return null;}}protected D onLoadInBackground() {return loadInBackground();
}
public abstract D loadInBackground();

很明显,最后执行的将是AsyncTaskLoader子类中的实现方法loadInBackground(),我们拿一个它的子类CursorLoader来看看,执行了什么语句:

public Cursor loadInBackground() {Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,mSelectionArgs, mSortOrder);if (cursor != null) {// Ensure the cursor window is filledcursor.getCount();cursor.registerContentObserver(mObserver);}return cursor;
}

查询数据库数据并且把结果cursor返回到AsyncTaskLoader中。好了,这样异步加载的线程就结束了,得到的结果将在onPostExecute()中进行下一步的处理:

protected void onPostExecute(D data) {if (DEBUG) Slog.v(TAG, this + " onPostExecute");try {AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);} finally {mDone.countDown();}}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 {mLastLoadCompleteTime = SystemClock.uptimeMillis();mTask = null;if (DEBUG) Slog.v(TAG, "Delivering result");deliverResult(data);}}
}

是不是感觉这个deliverResult(data)很熟悉,没错,我们在Loader中见过它的身影,但是这里执行的却并不是Loader中的deliverResult(data),而是CursorLoader中的deliverResult(data),看看它又干了什么:

public void deliverResult(Cursor cursor) {if (isReset()) {// An async query came in while the loader is stoppedif (cursor != null) {cursor.close();}return;}Cursor oldCursor = mCursor;mCursor = cursor;if (isStarted()) {super.deliverResult(cursor);}if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {oldCursor.close();}}

这个isStarted()是Loader中的方法,判断当前Loader的状态是mStarted,则调用Loader中的deliverResult()方法,执行它后,会回调LoaderManager中的onLoadComplete(),然后再回调一次,这个数据就回到LoaderCallbacks的实现方法onLoadFinished()中,这样我们就可以随心所欲的操作最后的数据结果了。如果当前Loader状态是mAbandoned,mReset,那么说明当前的Loader已经不需要加载数据了,放弃数据并释放资源。
那么,这里还有一个问题。到底谁调用onForceLoad()方法开启异步任务呢?

Loader.javapublic void forceLoad() {onForceLoad(); 
}

在Loader的子类CursorLoader中,有调用这个forceLoad()方法:

@Overrideprotected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}
}

谁调用CursorLoader中的onStartLoading()?

Loader.javapublic final void startLoading() {mStarted = true;mReset = false;mAbandoned = false;onStartLoading();
}

最后发现其实是在LoaderManager中的start()方法调用了startLoading(),由此,数据加载开始了,LoaderManager在其中起了一个Loader大管家的作用,它决定了什么时候启动、停止、保持、重启、关闭它的Loaders。
刚开始时,我们是从LoaderManager的initLoader()方法中开始入手研究的,其中涉及到了Loader,AsyncTaskLoader ,CursorLoader以及它们相关的内部类和接口,它们主要解决了异步的问题,并提供了单个数据加载的模板。统筹调度与管理主要还是在LoaderManager中。
开始研究LoaderManager之前,我们先记住单个数据的加载工作是在Loader的startLoading()中开始的。LoaderInfo实现了Loader内部的两个接口,数据加载结束后,Loader会回调LoaderInfo中的方法。了解了这两点,我们可以着手看LoaderManager了。
LoaderInfo的几个变量:
LoaderManager.LoaderCallbacks<Object> mCallbacks;Loader<Object> mLoader;public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {mId = id;mArgs = args;mCallbacks = callbacks;}

如上,LoaderInfo用mLoader来保存当前Loader,我们可以认为LoaderInfo是对Loader的进一步封装,除此之外,它的实例化还需要一个LoaderCallbacks对象,从我们的例子来看,这个LoaderCallbacks对象其实就是new CursorLoader(),为什么是这个呢,后面会讲到。
LoaderManager的几个变量:
<span style="font-size:14px;">final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
</span>
mLoaders是一个数据集合,它保存了所有的LoaderInfo对象(封装了Loader),也就是保存了所有的Loader对象。
了解了这几个变量,那来看看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;}

试图从集合mLoaders中加载已经存在的LoaderInfo对象,如果有,则直接返回LoaderInfo中保存的Loader对象。如果没有,则新创建一个LoaderInfo用来封装Loader,并把此LoaderInfo对象保存在集合中。

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;}
}

创建新的LoaderInfo对象,看看这个callback.onCreateLoader(id, args),callback就是传进来的LoaderCallbacks对象,所以这个onCreateLoader()调用的是我们自己实现的LoaderCallbacks里面的方法,所以说这个Loader对象现在是new CursorLoader()。

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;
}

把新建的LoaderInfo对象加入到集合中,如果下次需要加载同样的数据的话,就从集合中查找并直接返回LoaderInfo中携带的数据,避免了重复查询。

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 cycleinfo.start();}
}

在上面的方法中,每一个新创建的LoaderInfo都会执行info.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);mLoader.registerOnLoadCanceledListener(this);mListenerRegistered = true;}mLoader.startLoading();}}

mLoader.registerListener(mId, this);注册监听,其实就是完成了回调机制的布置。
mLoader.startLoading(),单个数据的加载工作开始。是不是有点熟悉,首尾相连了有没有。
好了,至此,LoaderManager的大致工作流程,我们已经跟踪一遍。现在我们简单再总结一下。
   从我们自己实现的LoaderManager.LoaderCallbacks开始,它有两个方法:
onCreateLoader():生成Loader的子类对象,返回给LoaderManager进行集中管理。
onLoadFinished():LoaderManager决定Loader对象的启动、停止、保持、重启、关闭,如果得到数据,这个方法就会被回调,我们只需要在这里操作得到的数据就ok了。
补充:

LoaderManagervoid doStart() {if (DEBUG) Log.v(TAG, "Starting in " + this);if (mStarted) {RuntimeException e = new RuntimeException("here");e.fillInStackTrace();Log.w(TAG, "Called doStart when already started: " + this, e);return;}mStarted = true;// Call out to sub classes so they can start their loaders// Let the existing loaders know that we want to be notified when a load is completefor (int i = mLoaders.size()-1; i >= 0; i--) {mLoaders.valueAt(i).start();}
}Activityprotected void onStart() {if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);mCalled = true;if (!mLoadersStarted) {mLoadersStarted = true;if (mLoaderManager != null) {mLoaderManager.doStart();} else if (!mCheckedForLoaderManager) {mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);}mCheckedForLoaderManager = true;}getApplication().dispatchActivityStarted(this);}

每次Activity的生命周期到onStart时,都会重新加载LoaderInfo集合中的所有Loader数据,见mLoaderManager.doStart();






 


这篇关于android之LoaderManager原理分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/571568

相关文章

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N

MyBatis-Plus 与 Spring Boot 集成原理实战示例

《MyBatis-Plus与SpringBoot集成原理实战示例》MyBatis-Plus通过自动配置与核心组件集成SpringBoot实现零配置,提供分页、逻辑删除等插件化功能,增强MyBa... 目录 一、MyBATis-Plus 简介 二、集成方式(Spring Boot)1. 引入依赖 三、核心机制

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、