本文介绍Loader, CursorLoader, AsyncTaskLoader 和 LoaderManager。先给出它们的使用示例,然后再对它们的架构和原理进行分析。
目录
1. Loader简介
2. Loader使用示例
3. Loader架构和原理
Loader简介
Loader:是一个执行异步数据加载的抽象类。它是AsyncTaskLoader的父类,CursorLoader的祖父类。
AsyncTaskLoader:它是继承于Loader的抽象类。在AsyncTaskLoader中有AsyncTask用来执行异步工作。
CursorLoader:它是AsyncTaskLoader的子类。它可以查询ContentResolver然后返回一个Cursor,同时CursorLoader内包含ContentObserver对象来监听Cursor数据的变化。
LoaderManager:它是一个抽像类,作用是用来管理Loader。LoaderManager关联到一个Activity或Fragment;例如,在Activity中通过getLoaderManager()可以获取到一个LoaderManager对象。
LoaderManager.LoaderCallbacks:它提供了与"Activity或Fragment"交互的接口。LoaderCallbacks中共包行三个接口:
onCreateLoader():根据所给出的ID,初始化并返回一个新的加载器。
onLoadFinished():当一个先前被创建的加载器完成了它的加载过程时被调用。
onLoaderReset():当一个先前被创建的加载器被重置时被调用,然后使加载器的数据无效。
总结:Loader, AsyncTaskLoader和CursorLoader都是用来提供异步加载的类;而LoaderManager则提供了管理这些异步加载类的接口。此外,Activity或Fragment保护了获取LoaderManager的接口,通过这样的接口,就可以在Activity或Fragment中实现异步加载任务!
Loader使用示例
下面通过一则示例演示Fragment中执行异步加载的完整流程!该示例中,会在FragmentActivity中采用异步加载的方式查找并显示数据库中所有的图片的相关信息。
点击查看:Loader示例的完整源码
1. 获取LoaderManager,并执行initLoader()
public class LoaderTest extends FragmentActivity
implements LoaderCallbacks<Cursor>, OnItemClickListener {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView)findViewById(android.R.id.list);
simpleCursorAdapter = new SimpleCursorAdapter(
this,
R.layout.list_item,
null,
STORE_IMAGES,
new int[] { R.id.item_title, R.id.item_value}
);
simpleCursorAdapter.setViewBinder(new ImageLocationBinder());
listView.setAdapter(simpleCursorAdapter);
// 注意此处是getSupportLoaderManager(),而不是getLoaderManager()方法。
getSupportLoaderManager().initLoader(0, null, this);
listView.setOnItemClickListener(this);
}
}
说明:需要重点关注的是getSupportLoaderManager(),它的作用是获取LoaderManager。然后,通过initLoader()初始化Loader。
initLoader()的完整声明如下:
public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback);
说明:
(01) id是Loader的唯一标识。
(02) args是Bundle,这里赋值位null。
(03) callback是回调函数。后面会给出回调的实现。
2. 实现LoaderManager.LoaderCallbacks接口
LoaderCallbacks中包行onCreateLoader(), onLoaderReset(), onLoadFinished()三个接口。下面是它们的实现:
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
CursorLoader cursorLoader = new CursorLoader(
this,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
STORE_IMAGES,
null,
null,
null);
return cursorLoader;
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
simpleCursorAdapter.swapCursor(null);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
simpleCursorAdapter.swapCursor(cursor);
}
说明:
(01) onCreateLoader()会在初始化Loader之后执行,它的作用是返回一个Loader对象。
(02) 而onLoadFinished()是当异步加载完成之后会被执行,当Loader加载完数据时,我们会通过 simpleCursorAdapter.swapCursor(cursor)将simpleCursorAdapter切换到新的Cursor对象(即,加载完后保存数据的对象)。不同于changeCursor(),changeCursor()在切换到新的Cursor之后,会将旧的Cursor关闭掉;而swapCursor()切换到新的Cursor之后,不会关闭旧的Cursor,如果旧的Cursor存在的话,它会返回旧的Cursor。
(03) onLoaderReset()则是当异步加载起被重置时会被调用。当Loader被重置会,我们通过simpleCursorAdapter.swapCursor(null)将Cursor设为null。
3. 其他操作
经过上面两步之后,已经能成功的使用异步加载数据了。得到的数据保存在SimpleCursorAdapter对象中,接下来需要做的就是在SimpleCursorAdapter中显示数据。
在onCreate()中,我们通过simpleCursorAdapter.setViewBinder(new ImageLocationBinder())设置了SimpleCursorAdapter的ViewBinder对象。下面看看ViewBinder中是如何显示的:
// 将图片的位置绑定到视图
private class ImageLocationBinder implements ViewBinder {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (columnIndex == 1) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
String path = cursor.getString(column_index);
((TextView)view).setText("Location: "+path);
return true;
}
return false;
}
}
至此,数据的异步加载示例差不多介绍完了。总的来说很简单,先获取LoaderManager对象,然后通过initLoader()初始化Loader。接着,需要作的是实现LoaderCallbacks接口!LoaderCallbacks接口中,需要重点关注的是onCreateLoader()和onLoadFinished()接口!
Loader架构和原理
在Loader简介中,我们大致介绍了Loader, LoaderManager各个类的作用。这里,我们分析一下它们的源码来查看它们是如何工作的。主要解决的有以下几个问题:
(01) Loader的异步加载原理。也就是说Loader异步是通过什么实现的?是一个Thread,还是AsyncTask,还是其他的什么方式?
(02) onCreateLoader()和onLoadFinished()是如何回调的。尤其是onLoadFinished(),当数据加载完时,是如何回调的?
(03) 当发生Activity或Fragment被重新加载(例如,发生旋屏动作)时,Loader不会重新加载,它里面的数据仍然存在。这是如何做到的?
(04) Loader异步加载器,能够自动更新数据。它又是如何做到的?
1. 异步加载原理
涉及到的文件:
frameworks/base/core/java/android/app/LoaderManager.java
frameworks/base/core/java/android/content/Loader.java
frameworks/base/core/java/android/content/AsyncTaskLoader.java
frameworks/base/core/java/android/content/CursorLoader.java
1.1 initLoader()
LoaderManager.java中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;
}
说明:
(01) initLoader()会现通过mLoaders.get(id)来根据id获取LoaderInfo对象。如果Activity或Fragment第一次调用initLoader(),则获取到的LoaderInfo对象为null。
(02) 如果info为null,则执行createAndInstallLoader()来创建LoaderInfo对象。
1.2 createAndInstallLoader()
LoaderManager.java中createAndInstallLoader()源码如下:
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;
}
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;
}
}
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();
}
}
说明:
(01) createAndInstallLoader()会分别调用createLoader()和installLoader()接口。
(02) createLoader()会新建LoaderInfo对象,然后调用callback.onCreateLoader()接口。这里的onCreateLoader()就是我们实现的LoaderCallbacks接口中的onCreateLoader()接口。
(03) initLoader()中会调用info.start()接口。
1.3 LoaderInfo中的start()
LoaderManager.java中的内部类LoaderInfo的源码如下:
final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
Loader.OnLoadCanceledListener<Object> {
final int mId;
final Bundle mArgs;
LoaderManager.LoaderCallbacks<Object> mCallbacks;
Loader<Object> mLoader;
...
public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
mId = id;
mArgs = args;
mCallbacks = callbacks;
}
void start() {
if (mRetaining && mRetainingStarted) {
mStarted = true;
return;
}
if (mStarted) {
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();
}
}
...
}
说明:start()中一个非常重要的成员是mLoader。它就是onCreateLoader()返回的Loader对象。start()会调用mLoaders.startLoading()。
1.4 startLoading()
下面查看startLoading的相关代码。查看代码时,请记得:Loader.java, AsyncTaskLoader和CursorLoader这三者之间存在继承关系!
// 在Loader.java中
public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}
// 在CursorLoader.java中
@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
说明:startLoading()是在Loader.java中定义的,它调用了onStartLoading()。而CursorLoader.java覆盖了onStartLoading()函数。在onStartLoading()中,此时的mCursor是为null;所以,它会执行forceLoad()。
1.5 forceLoad()
下面查看forceLoad的相关代码。
// 在Loader.java中
public void forceLoad() {
onForceLoad();
}
// 在AsyncTaskLoader.java中
@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
mTask = new LoadTask();
if (DEBUG) Slog.v(TAG, "Preparing load: mTask=" + mTask);
executePendingTask();
}
// 在AsyncTaskLoader.java中
void executePendingTask() {
if (mCancellingTask == null && mTask != null) {
if (mTask.waiting) {
mTask.waiting = false;
mHandler.removeCallbacks(mTask);
}
if (mUpdateThrottle > 0) {
long now = SystemClock.uptimeMillis();
if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
// Not yet time to do another load.
if (DEBUG) Slog.v(TAG, "Waiting until "
+ (mLastLoadCompleteTime+mUpdateThrottle)
+ " to execute: " + mTask);
mTask.waiting = true;
mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
return;
}
}
if (DEBUG) Slog.v(TAG, "Executing: " + mTask);
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}
说明:forceLoad()会调用onForceLoad()。而onForceLoad()中会新建LoadTask对象,然后执行executePendingTask()。在executePendingTask()中会调用LoadTask对象的executeOnExecutor()。
1.6 LoadTask
LoadTask是AsyncTaskLoader的内部类。实际上,它是AsyncTask的子类。在介绍AsyncTask的文章中,我们知道executeOnExecutor()会将任务提交到线程池中去执行,而这个被提交到线程池的任务会执行AsyncTask的doInBackground()。
// AsyncTaskLoader.java中
final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
private final CountDownLatch mDone = new CountDownLatch(1);
boolean waiting;
@Override
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)", ex);
return null;
}
}
/* Runs on the UI thread */
@Override
protected void onPostExecute(D data) {
if (DEBUG) Slog.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}
...
}
说明:LoadTask的doInBackground()会调用onLoadInBackground()。
1.7 onLoadInBackground()
下面查看onLoadInBackground()的相关代码。
// AsyncTaskLoader.java中
protected D onLoadInBackground() {
return loadInBackground();
}
// CursorLoader.java中
@Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder, mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
cursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
说明:loadInBackground()会执行查询。这就是后台任务真正执行的动作所在!
至此,我们已经回答了第一个问题:Loader的异步加载原理。实际上,Loader的异步加载是通过AsyncTask启动一个后台任务,在后台任务中再执行query()查询操作!
2. onLoadFinished是如何被回调的
我们知道,当AsyncTask中的任务执行完时,会通过onPostExecute()反馈执行结果。下面,我们看看AsyncTask反馈的结果中是如何调用onLoadFinished()的。
2.1 onPostExecute
下面看看onPostExecute()的相关代码。
// AsyncTaskLoader.java中
@Override
protected void onPostExecute(D data) {
if (DEBUG) Slog.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}
// AsyncTaskLoader.java中
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);
}
}
}
说明:onPostExecute()会执行dispatchOnLoadComplete(),而后者会调用deliverResult()来分发消息。
2.2 deliverResult()
// CursorLoader.java中
@Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
// Loader.java中
public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}
说明:CursorLoader.java中的deliverResult()会通过super.deliverResult()调用父类的deliverResult(),也就是会执行Loader.java中的deliverResult()。最终会执行mListener.onLoadComplete()。mListener是什么呢?它是我们在执行LoaderManager.java的start()函数时,通过mLoader.registerListener(mId, this)注册到Loader上的。也就是说,mListener是LoaderManager中的LoaderInfo对象。
2.3 LoaderInfo中的onLoadComplete()
@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) {
if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active");
return;
}
LoaderInfo pending = mPendingLoader;
if (pending != null) {
mPendingLoader = null;
mLoaders.put(mId, null);
destroy();
installLoader(pending);
return;
}
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
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();
}
}
void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
String lastBecause = null;
if (mActivity != null) {
lastBecause = mActivity.mFragments.mNoTransactionsBecause;
mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
}
try {
if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
mCallbacks.onLoadFinished(loader, data);
} finally {
if (mActivity != null) {
mActivity.mFragments.mNoTransactionsBecause = lastBecause;
}
}
mDeliveredData = true;
}
}
说明:onLoadComplete()会调用callOnLoadFinished(),而callOnLoadFinished()最终会调用onLoadFinished()。这就是我们实现的LoaderCallbacks接口中的onLoadFinished()。
至此,我们也搞明白了第二个问题onLoadFinished()是如何被回调的。当AsyncTask任务执行完时,会调用onPostExecute();而onPostExecute()会通过LoaderManager,LoaderManager最终会调用onLoadFinished()。
3. 当Activity或Fragment被重新加载时,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;
}
说明:initLoader()会首先调用mLoaders.get(id)来获取id对应的LoaderInfo对象。如果Activity或Fragment是由于旋屏而发生重载,那么通过id获取到的LoaderInfo就不是null。那么initLoader()就会调用info.callOnLoadFinished()。
4. Loader异步加载器自动更新数据
Loader自动更新数据是通过ContentObserver来实现的。
4.1 注册ContentResolver
// CursorLoader.java中
@Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder, mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
cursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
说明:在执行query()查询之后,如果得到的Cursor对象不是null。则通过cursor.registerContentObserver(mObserver)来监听该cursor的内容。
4.2 注册mObserver
// CursorLoader.java中
public CursorLoader(Context context) {
super(context);
mObserver = new ForceLoadContentObserver();
}
// CursorLoader.java中
public CursorLoader(Context context, Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
super(context);
mObserver = new ForceLoadContentObserver();
mUri = uri;
mProjection = projection;
mSelection = selection;
mSelectionArgs = selectionArgs;
mSortOrder = sortOrder;
}
说明:在创建CursorLoader对象时,就创建了mObserver,它是ForceLoadContentObserver对象。
4.3 ForceLoadContentObserver
// Loader.java
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
super(new Handler());
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
onContentChanged();
}
}
// Loader.java中
public void onContentChanged() {
if (mStarted) {
forceLoad();
} else {
// This loader has been stopped, so we don't want to load
// new data right now... but keep track of it changing to
// refresh later if we start again.
mContentChanged = true;
}
}
说明:ForceLoadContentObserver是Loader.java的内部类,它继承于ContentObserver。当数据发生变化时,它会回调onChange(),进而调用onContentChanged()。而onContentChanged()则会执行forceLoad()。forceLoad()在前面已经介绍过了,它会启动AsyncTask被执行query查询操作!
至此,几个问题都回答完了。Loader, AsyncTaskLoader, CursorLoader和LoaderManager的相互关系也整理完了!谢谢观赏!