在看Android的文档时,看到了这么一个东西:

究竟是什么东西呢?

Introduced in Android 3.0, loaders make it easy to asynchronously load
data in an activity or fragment. Loaders have these characteristics:

1、They are available to every Activity and Fragment.
//支持Activity和Fragment

2、They provide asynchronous loading of data. //异步下载

3、They monitor the source of their data and deliver new results when
the content changes. //当数据源改变时能及时通知客户端

4、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时自动重连接

看来这东西蛮强大的,开始我的探索之路吧.

先简单看一下它的用法先:

/** * 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提供的实例代码,有删减。

从代码上看来,通过实现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循环,真相大白了..

最后总结一下:

1、异步是通过AsynTaskLoader来实现的。

2、通过观察者模式来实现监控数据的变化.

3、通过Activity生命周期中的onStart来实现自动重连接.

更多相关文章

  1. Android百度地图基础实现(标记+GPS)
  2. 在android里面使用自定义字体
  3. Android(安卓)格式化内部存储
  4. Android(安卓)格式化内部存储
  5. 【Android】安卓中常用的图片加载方法
  6. Android获取设备状态栏status bar高度的正确姿势
  7. 如何让Android横竖屏切换时不销毁当前activity
  8. Android(安卓)6.0 Ethernet流程分析记录
  9. android configChanges

随机推荐

  1. Android(安卓)Studio,gradle project sync
  2. eclipse中加入写好的android工程和出现的
  3. Android提交数据到服务器的两种方式四种
  4. 使用国内开源镜像下载Android(安卓)SDK
  5. android源代码分布结构
  6. Eclipse无法显示Android设计界面解决方案
  7. Chromecast
  8. Android(安卓)百分比布局详解及实例代码
  9. Android unspecified' depends on one or
  10. Android安全知识库