在看Android的文档时,看到了这么一个东西: Loaderjava
到底是什么东西呢?android
Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:数据库
一、They are available to every Activity and Fragment. //支持Activity和Fragment异步
二、They provide asynchronous loading of data. //异步下载async
三、They monitor the source of their data and deliver new results when the content changes. //当数据源改变时能及时通知客户端ide
四、They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data. //发生configuration change时自动重链接函数
看来这东西蛮强大的,开始个人探索之路吧.post
先简单看一下它的用法先:性能
/** * Demonstration of the use of a CursorLoader to load and display contacts * data in a fragment. */ public class LoaderCursor extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager fm = getFragmentManager(); // Create the list fragment and add it as our sole content. if (fm.findFragmentById(android.R.id.content) == null) { CursorLoaderListFragment list = new CursorLoaderListFragment(); fm.beginTransaction().add(android.R.id.content, list).commit(); } } public static class CursorLoaderListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; // If non-null, this is the current filter the user has provided. String mCurFilter; @Override public void onActivityCreated(Bundle savedInstanceState) { mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); setListAdapter(mAdapter); getLoaderManager().initLoader(0, null, this); } @Override public void onListItemClick(ListView l, View v, int position, long id) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: " + id); } // These are the Contacts rows that we will retrieve. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); // The list should now be shown. if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } } }
这里是Android提供的实例代码,有删减。this
从代码上看来,经过实现LoaderManager.LoaderCallbacks就好了.
在onCreateLoader里面实现你要请求的耗时操做,当异步线程操做完成以后就会从onLoadFinished返回数据.
用起来是否是很简单呢?下面具体来看一下它是怎么作到的吧.
getLoaderManager()是定义在Activity类的一个方法,返回类型LoaderManager,但这只是个接口,它真正的实现类是谁呢?
继续往下走,看到这个LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create),方法时,答案便揭晓了.
下面咱们来看看LoaderManager相关的类结构,省略了不少东西,但不影响咱们的分析.
如今咱们来到了LoaderManagerImp的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 (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; }
这是一个新的Loader,那么info应该是null,转入执行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; } } 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; } 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(); } }
createLoader把必要的信息都封装在LoaderInfo类里面,留意如下这一行:
callback.onCreateLoader(id,arg),这里正是咱们上面在客户端实现接口LoaderCallback的那个方法.
接着调用installLoader,这个方法把此次Loader的信息put进mLoader这个SparseArrayCompat中,这个对象能够理解为一个Map,它的性能比Map要好.
mStarted的值是true,它是在getLoaderManager的时候在Activity中传进来的true值.
好了,下面进入LoaderInfo的start方法了.
void start() { if (mLoader != null) { if (!mListenerRegistered) { mLoader.registerListener(mId, this); mListenerRegistered = true; } mLoader.startLoading(); } }
mLoader就是在客户端实现的那个Loader,回到咱们刚开始时的例子,它就是一个CursorLoader.
在分析CursorLoader的startLoading以前,咱们先看一下这些Loader的类结构先:
从这些类的名称看来,真正实现了异步传输功能的类应该就是AsyncTaskLoader了,事实是否是这样呢?
继续深刻下去:
这里的startLoading是调用了Loader类的方法,下文中我会用这样的方法来标识方法是属于哪一个类的: 如Loader –> startLoading
Loader: public final void startLoading() { mStarted = true; mReset = false; mAbandoned = false; onStartLoading(); } CursorLoader: protected void onStartLoading() { if (mCursor != null) { deliverResult(mCursor); } if (takeContentChanged() || mCursor == null) { forceLoad(); } } AsynTaskLoader: protected void onForceLoad() { super.onForceLoad(); cancelLoad(); mTask = new LoadTask(); if (DEBUG) Slog.v(TAG, "Preparing load: mTask=" + mTask); executePendingTask(); }
终于看到了LoadTask关键字啦,答案就要揭晓啦.
AsyncTaskLoader: final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable { private final CountDownLatch mDone = new CountDownLatch(1); // Set to true to indicate that the task has been posted to a handler for // execution at a later time. Used to throttle updates. boolean waiting; /* Runs on a worker thread */ @Override protected D doInBackground(Void... params) { if (DEBUG) Slog.v(TAG, this + " >>> doInBackground"); try { D data = AsyncTaskLoader.this.onLoadInBackground(); return data; } catch (OperationCanceledException ex) { } } /* 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(); } } } AsyncTaskLoader: protected D onLoadInBackground() { return loadInBackground(); } CursorLoader: public Cursor loadInBackground() { try { Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder, mCancellationSignal); if (cursor != null) { // Ensure the cursor window is filled cursor.getCount(); registerContentObserver(cursor, mObserver); } return cursor; } finally { synchronized (this) { mCancellationSignal = null; } }
LoadTask原来是个AsyncTask类型,看到这里你们你们应该以为有种豁然的感受了吧.
在ForceLoad里面启动该线程,开始执行doInBackground,回调CursorLoader里面的loadInBackgroud,这个方法里面执行真正的耗时操做,
执行完以后一层一层返回,接着调用onPostExecute方法.
好了,如今数据总算是拿到了.
接着执行,把获取的数据往回调.
LoadTask -> onPostExecute
----->
AsynTaskLoader-> dispatchOnLoadComplete
----->
Loader->deliverResult
回调前面注册的loadComplete:
LoaderInfo -> onLoadComplete
---->
LoaderInfo ->callOnLoadFinished
把数据回调给客户端
mCallbacks.onLoadFinished(loader, data);
到这里就完美解释了Loader的特色2,异步
第三点当数据源改变时能及时通知客户端又是如何体现的呢?
这里用了观察者模式来实现.咱们先看一下CursorLoader的构造函数:
mObserver = new ForceLoadContentObserver();
这个ForceLoadContentObserver是什么东西呢?
ForceLoadContentObserver继承了ContentObserver,这是Android内部的一个对象,继承了它,就能享受到数据变化时能够接收到通知(也就是观察者中的Subject),这里相似于数据库中的触发器.
先往下看:
在CursorLoader->loadInBackground方法中有这么一句:
registerContentObserver(cursor, mObserver);//注册观察者
答案揭晓了.
注册观察者后,当对应的URI发生变化是,会触发onChange方法
public void onChange(boolean selfChange) { onContentChanged(); } 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; } }
对于forceLoad方法前面已经提升过了,你们应该还有印象吧.
最后一个问题,也就是第四点:如何作到在configuration change自动重连接的呢?
只要能回答这两个问题,这个问题就解决了.
<1>loader如何在configuration change以前保存数据?
<2>loader如何在configuration chage以后恢复数据并继续load?
LoaderManager:
还记得吗?Loader建立之初,在LoaderManagerImp->installLoader方法里面,
mLoaders.put(info.mId, info);
Info 是LoaderInfo对象,里面封装了Loader的相关信息,表示这个LoaderInfo的Key是mId.
就是在这里保存了loader.这样就回答了问题<1>
对于问题二,首先咱们来了解一下configuration change发生以后会发生什么事情呢?
还记得这个生命周期图吗,Fragment的也是差很少的.
当configuration change发生以后,会先把原来的Activity销毁掉,而后再从新构建一个,
也就是会重走一遍onCreate->onStart->onResume的过程.
好了,明白这个以后,我在onStart方法里面找到了线索.
Activity: 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); } LoaderManagerImp: void 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 complete for (int i = mLoaders.size()-1; i >= 0; i--) { mLoaders.valueAt(i).start(); } }
留意doStart的For循环,真相大白了..
最后总结一下:
一、异步是经过AsynTaskLoader来实现的。
二、经过观察者模式来实现监控数据的变化.
三、经过Activity生命周期中的onStart来实现自动重链接.