概述
在实际项目,一般很少直接在主线程中进行数据库操作。为解决该问题可以使用AsyncQueryHandler,该类中有一系列的startXXX方法,可以在子线程对数据库进行CRUD操作。但如果我们加载的数据不在数据库中时,AsyncQueryHandler就显得无能为力了。此时可以使用Loader。
Loader设计用于从数据源加载某类数据(如对象)。数据源可以是磁盘、数据库、ContentProvider、网络或者另一个进程。loader可以在不阻塞主线程的情况下获取并发送数据给接收者。Android提供了Loader,AsyncTaskLoader与CursorLoader三种内置类型。
作为基类,Loader本身没有多大用处,它定义了供LoaderManager与loader通讯的api。
AsyncTaskLoader是一个抽象的Loader。它使用AsyncTask将数据加载任务转移到其他线程上。我们用的大部分都是它的子类。
CursorLoader,它是AsyncTaskLoader的子类,它借助ContentResolver从ContentProvider中加载数据。
以上内容摘自王明发译的《Android编程权威指南》
Loader
整个Loader类中没有进行任何关于加载数据的处理逻辑,它只是定义了Loader的三个状态——start,abandon,reset——以及两个监听器(OnLoadCompleteListener,OnLoadCanceledListener)和一个ContentObserver。
ContentObserver在Loader中只是定义,在CursorLoader中才使用。CursorLoader#loadInBackground中有:
- cursor.registerContentObserver(mObserver);
因此,到cursor发生变化时,会执行ForceLoadContentObserver#onChange(),它又会调用onContentChanged()方法:
- public void onContentChanged() {
- if (mStarted) {
- forceLoad();
- } else {
-
- mContentChanged = true;
- }
- }
这里就可以看出mContentChanged的作用:记录loader停止后数据是否发生了变化,为了在再启动时能去刷新数据。
对于commitContentChanged(),注释上有一句话:Call this when you have completely processed a load without it being cancelled。这就是说:当你完全处理了一个没有被取消的loader时调用该方法。因此,mProcessingChange表示是否正在处理changed
- public void commitContentChanged() {
- mProcessingChange = false;
- }
同样takeContentChanged的作用也很好说了:获取在停止时是否有内容变化。
- public boolean takeContentChanged() {
- boolean res = mContentChanged;
- mContentChanged = false;
- mProcessingChange |= res;
- return res;
- }
Loader为三种状态提供了一系列的方法:如获取方法isStarted(),isAbandoned(),isReset()。修改方法如下:
- public final void startLoading() {
-
- onStartLoading();
- }
- protected void onStartLoading() {
- }
- public boolean cancelLoad() {
- return onCancelLoad();
- }
- protected boolean onCancelLoad() {
- return false;
- }
-
-
-
-
- public void forceLoad() {
- onForceLoad();
- }
- protected void onForceLoad() {
- }
-
-
-
-
- public void stopLoading() {
- mStarted = false;
- onStopLoading();
- }
-
-
-
- protected void onStopLoading() {
- }
-
-
-
-
- public void abandon() {
- mAbandoned = true;
- onAbandon();
- }
- protected void onAbandon() {
- }
-
-
-
-
- public void reset() {
- onReset();
-
- }
- protected void onReset() {
- }
方法很多,只有forceLoad与cancelLoad能调用,其余的都是LoaderManager调用的。
从上面可以看出,Loader只是定义了一些操作自身的函数,方便LoaderManager使用。它并没有真正加载数据,一般加载数据都是通过继承AsyncTaskLoader实现的。
LoaderManager
LoaderManager主要用来管理loader。可以在activity/fragment中调用getLoaderManager()获取LoaderManager的实例。LoaderManager只是一个接口,它的真正实现是其内部类<LoaderManagerImpl。
initLoader()
通过该方法可以完成loader的初始化。LoaderManager会根据loader的状态自动调用第三个参数(以下称回调)中的方法。这也是回调的作用:根据loader的状态,执行不同的操作。
第一个参数是指定当前loader的id,这是该loader的唯一标识。当id相同时,loader就相同。
第二个参数是方便在初始化loader时传递参数。它最终会传递到回调中onCreateLoader()中成为第二个参数。
initLoader()的部分代码如下:
- public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
- ……
- LoaderInfo info = mLoaders.get(id);
- ……
- if (info == null) {
-
- info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
- } else {
- info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
- }
- if (info.mHaveData && mStarted) {
-
- info.callOnLoadFinished(info.mLoader, info.mData);
- }
- return (Loader<D>)info.mLoader;
- }
其中mLoaders存储的是当前所有激活的loader实例(These are the currently active loaders)。
最后一个判断表明:如果当前的loader有数据,并且已经启动,就会直接调用LoadInfo.callOnLoadFinished(),而在该方法中会调用到LoaderCallbacks.onLoadFinished()。
当info为null时调用createAndInstallLoader(),同时将initLoader()中的所有参数传递进去。createAndInstallLoader()主要代码如下:
- LoaderInfo info = createLoader(id, args, callback);
- installLoader(info);
再看一下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;
- }
首先创建LoaderInfo,并调用回调中的onCreateLoader创建Loader。同时为info中的mLoader赋值。
在installLoader()中,会调用mLoaders.put(info.mId, info),这里的info.mId就是initLoader()中的第一个参数。这样就将新建的loader存储到了mLoaders中。
同时installLoader()还会调用info.start()。这是一个很重要的方法 ,因为它会调用Loader.startLoading()。代码为:
- void start() {
- ……
- mStarted = true;
- ……
- if (mLoader == null && mCallbacks != null) {
- mLoader = mCallbacks.onCreateLoader(mId, mArgs);
- }
- if (mLoader != null) {
- ……
- if (!mListenerRegistered) {
- mLoader.registerListener(mId, this);
- mLoader.registerOnLoadCanceledListener(this);
- mListenerRegistered = true;
- }
- mLoader.startLoading();
- }
- }
当mLoader不为null时,会调用mLoader.startLoading,同时为mLoader设置监听器。再结合createLoader()来看,mLoader的值就是LoaderCallbacks.onCreateLoader()的返回值。
从这里开始已经由LoaderManager进入到了Loader中。
restartLoader
当使用initLoader()时,如果指定的id的loader已经存在,系统会重用该loader;否则系统会创建一个新的。但是如果想丢弃掉旧的loader,应该使用restartLoader()。
AsyncTaskLoader
异步执行加载操作。
从上面的LoaderManager#initLoader()中可以看出,最后会执行的是onStartLoading。但AsyncTaskLoader并没有重写这个方法,而是重写了onForceLoad。由Loader的分析发现,onForceLoad()的调用是由forceLoad()引起的。因此,需要重写Loader#onStartLoading并调用forceLoad()。这一点可以从CursorLoader中得到佐证。如下:
- protected void onStartLoading() {
- if (mCursor != null) {
- deliverResult(mCursor);
- }
- if (takeContentChanged() || mCursor == null) {
- forceLoad();
- }
- }
onForceLoad
- @Override
- protected void onForceLoad() {
- super.onForceLoad();
- cancelLoad();
- mTask = new LoadTask();
- executePendingTask();
- }
LoadTask为AsyncTask的子类。最后一个方法如下:
- void executePendingTask() {
- if (mCancellingTask == null && mTask != null) {
- ……
- mTask.executeOnExecutor(mExecutor, (Void[]) null);
- }
- }
在mTask的doInBackground中会调用onLoadInBackground(),它又调用loadInBackground(),这才是异步执行的地方。
mTask为AsyncTask,故加载完毕后会走onPostExecute中,又会调用dispatchOnLoadComplete(),其作用主要是对加载结果进行分发。如下:
- void dispatchOnLoadComplete(LoadTask task, D data) {
- if (mTask != task) {
-
- dispatchOnCancelled(task, data);
- } else {
- if (isAbandoned()) {
-
- onCanceled(data);
- } else {
- commitContentChanged();
- mLastLoadCompleteTime = SystemClock.uptimeMillis();
- mTask = null;
- deliverResult(data);
- }
- }
- }
在LoaderInfo#start()中,为Loader设置的OnLoadCompleteListener是LoaderInfo自身,因此上面的deliverResult会执行到LoaderInfo#onLoadComplete().有如下代码
- if (mData != data || !mHaveData) {
- mData = data;
- mHaveData = true;
- if (mStarted) {
- callOnLoadFinished(loader, data);
- }
- }
至此,Loader的加载过程结束。这中间唯一需要自己操作的就是继承AsyncTaskLoader时,必须重写onStartLoad()并在其中调用forceLoad(),不然整个加载过程不完整。
总结
LoaderManager通过id区分它所管理的loader。当两个loader的id相同时,便会被认为是同一个loader。
如果当前id的loader已经存在,那么不会创建新的loader,只是更新initLoader()中的回调。
如果当前id的loader已经存在,并且已经有数据,那么不会再次查询数据,而是直接将数据传递到回调中的onLoadFinished()中。
示例
一个完整的对AsyncTaskLoader的实现,有些东西基本上是固定的,而且与CursorLoader类似。例子来源于google的TODO-MVP-Loaders。如下:
- public class TasksLoader extends AsyncTaskLoader<List<Task>>
- implements TasksRepository.TasksRepositoryObserver{
- private TasksRepository mRepository;
- public TasksLoader(Context context, @NonNull TasksRepository repository) {
- super(context);
- checkNotNull(repository);
- mRepository = repository;
- }
-
- @Override
- public List<Task> loadInBackground() {
- return mRepository.getTasks();
- }
-
- @Override
- public void deliverResult(List<Task> data) {
- if (isReset()) {
- return;
- }
-
- if (isStarted()) {
- super.deliverResult(data);
- }
-
- }
-
- @Override
- protected void onStartLoading() {
- if (mRepository.cachedTasksAvailable()) {
- deliverResult(mRepository.getCachedTasks());
- }
- mRepository.addContentObserver(this);
-
- if (takeContentChanged() || !mRepository.cachedTasksAvailable()) {
-
- forceLoad();
- }
- }
-
- @Override
- protected void onStopLoading() {
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- onStopLoading();
-
- mRepository.removeContentObserver(this);
- }
-
- @Override
- public void onTasksChanged() {
- if (isStarted()) {
- forceLoad();
- }
- }
- }