zoukankan      html  css  js  c++  java
  • Android应用开发:LoaderManager在Activity/Fragment中的使用分析

    LoaderManager

    外部接口initLoader:起始

    1. public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
    2. if (mCreatingLoader) {
    3. throw new IllegalStateException("Called while creating a loader");
    4. }
    5. LoaderInfo info = mLoaders.get(id);
    6. if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
    7. if (info == null) {
    8. // Loader doesn't already exist; create.
    9. info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
    10. if (DEBUG) Log.v(TAG, " Created new loader " + info);
    11. } else {
    12. if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
    13. info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
    14. }
    15. if (info.mHaveData && mStarted) {
    16. // If the loader has already generated its data, report it now.
    17. info.callOnLoadFinished(info.mLoader, info.mData);
    18. }
    19. return (Loader<D>)info.mLoader;
    20. }
    1. mCreatingLoader代表当前LoaderManager正处于创建Loader的状态,这个时候进入initLoader属于冲突。

    2. LoaderInfo为LoaderManager内部保存Loader信息的一个类,mLoaders保存了此LoaderManager载入过的Loader,须要注意:一个ID相应一个Loader。

    3. info获取不到,说明这次是一个新的Loader进来,须要通过createAndInstallLoader进行创建和安装。參数即为initLoader的參数。

    4. 假设获取到了info,说明这是一个已经存在过的Loader,仅仅须要又一次对callback回调进行又一次赋值就可以。
    5. 若获取到的Loader已经開始。而且产生了有效数据。则运行LoaderInfo的callOnLoadFinished方法上报数据。

    6. 终于返回LoaderInfo中的Loader信息。

    LoaderInfo声明了Loader.OnLoadCompleteListener接口。而且保存了一个Loader的差点儿全部信息和状态。

    LoaderInfo的构造函数

    1. public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
    2. mId = id;
    3. mArgs = args;
    4. mCallbacks = callbacks;
    5. }

    createAndInstallLoader

    1. private LoaderInfo createAndInstallLoader(int id, Bundle args,
    2. LoaderManager.LoaderCallbacks<Object> callback) {
    3. try {
    4. mCreatingLoader = true;
    5. LoaderInfo info = createLoader(id, args, callback);
    6. installLoader(info);
    7. return info;
    8. } finally {
    9. mCreatingLoader = false;
    10. }
    11. }

    createLoader

    1. private LoaderInfo createLoader(int id, Bundle args,
    2. LoaderManager.LoaderCallbacks<Object> callback) {
    3. LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
    4. Loader<Object> loader = callback.onCreateLoader(id, args);
    5. info.mLoader = (Loader<Object>)loader;
    6. return info;
    7. }

    通过调用callback的onCreateLoader接口创建Loader。这样一个描写叙述Loader的LoaderInfo就被成功创建了。

    完毕了创建后,接下来是安装。

    installLoader

    1. void installLoader(LoaderInfo info) {
    2. mLoaders.put(info.mId, info);
    3. if (mStarted) {
    4. // The activity will start all existing loaders in it's onStart(),
    5. // so only start them here if we're past that point of the activitiy's
    6. // life cycle
    7. info.start();
    8. }
    9. }
    1. 将新的Loader装进mLoaders保存,凡是通过此LoaderManager管理过的Loader都会有记录的
    2. 即使是在Fragment中使用LoaderManager。其获取方式也是通过Fragment附属的Activity获取。而mStarted状态量与Activity的生命周期onStart/onStop有关。稍后做具体分析。
    3. 若Activity的生命周期处于onStart和onStop中,则开启Loader。

    LoaderInfo的start()

    1. void start() {
    2. if (mRetaining && mRetainingStarted) {
    3. // Our owner is started, but we were being retained from a
    4. // previous instance in the started state... so there is really
    5. // nothing to do here, since the loaders are still started.
    6. mStarted = true;
    7. return;
    8. }
    9. if (mStarted) {
    10. // If loader already started, don't restart.
    11. return;
    12. }
    13. mStarted = true;
    14. if (DEBUG) Log.v(TAG, " Starting: " + this);
    15. if (mLoader == null && mCallbacks != null) {
    16. mLoader = mCallbacks.onCreateLoader(mId, mArgs);
    17. }
    18. if (mLoader != null) {
    19. if (mLoader.getClass().isMemberClass()
    20. && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
    21. throw new IllegalArgumentException(
    22. "Object returned from onCreateLoader must not be a non-static inner member class: "
    23. + mLoader);
    24. }
    25. if (!mListenerRegistered) {
    26. mLoader.registerListener(mId, this);
    27. mListenerRegistered = true;
    28. }
    29. mLoader.startLoading();
    30. }
    31. }
    1. 假设Loader还处于上一个的開始状态中。就不做不论什么事情
    2. 假设已经開始了,不要重新启动
    3. 若Loader为null,则调用callback的接口再创建一个
    4. 注冊Loader的监听器。以id为唯一标识
    5. 启动Loader

    Loader的startLoading()

    1. public final void startLoading() {
    2. mStarted = true;
    3. mReset = false;
    4. mAbandoned = false;
    5. onStartLoading();
    6. }

    会启动Loader的onStartLoading。假设是复写的Loader或者AsyncTaskLoader,就是运行到了这里。是Loader在构造后运行的第一道工序。

    关于结果数据的Loader回调

    在LoaderInfo中给Loader注冊了回调,回调就是LoaderInfo本身。下边以AsyncTaskLoader为例进行分析。

    AsyncTaskLoader继承于Loader,同一时候其内部完毕了一个AsyncTask。

    AsyncTaskLoader的抽象接口

    1. public abstract D loadInBackground();

    事实上就是执行在其内部的AsyncTask中的doInBackground。

    PS: 继承于AsyncTaskLoader的Loader不会在构造后自己主动启动,须要覆写onStartLoading中运行forceLoad。此Loader才会在onStartLoading的生命周期时正常启动。

    在其内部的AsyncTask完毕后会在onPostExecute中调用AsyncTaskLoader的dispatchOnLoadComplete

    1. void dispatchOnLoadComplete(LoadTask task, D data) {
    2. if (mTask != task) {
    3. if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel");
    4. dispatchOnCancelled(task, data);
    5. } else {
    6. if (isAbandoned()) {
    7. // This cursor has been abandoned; just cancel the new data.
    8. onCanceled(data);
    9. } else {
    10. commitContentChanged();
    11. mLastLoadCompleteTime = SystemClock.uptimeMillis();
    12. mTask = null;
    13. if (DEBUG) Slog.v(TAG, "Delivering result");
    14. deliverResult(data);
    15. }
    16. }
    17. }
    • 假设完毕的是旧任务,则取消掉
    • isAbandoned是Loader的方法,通过主动调用Loader的abandon()方法能够放弃这个Loader的运行结果。假设此Loader已经被放弃。那么调用

      1. public void onCanceled(D data)

      处理被去掉的数据,AsyncTaskLoader中没有做不论什么处理,留给继承者自行决定是否处理

    • 排除以上两种异常情况。正常时会调用Loader的deliverResult处理数据结果。

      1. public void deliverResult(D data) {
      2. if (mListener != null) {
      3. mListener.onLoadComplete(this, data);
      4. }
      5. }

    之前分析过的LoaderInfo为自己的Loader注冊了监听器。实现是自己,这下子数据就传递回去了:

    1. @Override public void onLoadComplete(Loader<Object> loader, Object data) {
    2. if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
    3. if (mDestroyed) {
    4. if (DEBUG) Log.v(TAG, " Ignoring load complete -- destroyed");
    5. return;
    6. }
    7. if (mLoaders.get(mId) != this) {
    8. // This data is not coming from the current active loader.
    9. // We don't care about it.
    10. if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active");
    11. return;
    12. }
    13. LoaderInfo pending = mPendingLoader;
    14. if (pending != null) {
    15. // There is a new request pending and we were just
    16. // waiting for the old one to complete before starting
    17. // it. So now it is time, switch over to the new loader.
    18. if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending);
    19. mPendingLoader = null;
    20. mLoaders.put(mId, null);
    21. destroy();
    22. installLoader(pending);
    23. return;
    24. }
    25. // Notify of the new data so the app can switch out the old data before
    26. // we try to destroy it.
    27. if (mData != data || !mHaveData) {
    28. mData = data;
    29. mHaveData = true;
    30. if (mStarted) {
    31. callOnLoadFinished(loader, data);
    32. }
    33. }
    34. //if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this);
    35. // We have now given the application the new loader with its
    36. // loaded data, so it should have stopped using the previous
    37. // loader. If there is a previous loader on the inactive list,
    38. // clean it up.
    39. LoaderInfo info = mInactiveLoaders.get(mId);
    40. if (info != null && info != this) {
    41. info.mDeliveredData = false;
    42. info.destroy();
    43. mInactiveLoaders.remove(mId);
    44. }
    45. if (mActivity != null && !hasRunningLoaders()) {
    46. mActivity.mFragments.startPendingDeferredFragments();
    47. }
    48. }
    1. 若LoaderInfo已经通过destroy()进行过销毁。则不处理不论什么事情
    2. 若回传的Loader与LoaderInfo描写叙述的并非一个,也不做不论什么处理
    3. mPendingLoader在LoaderManager的restartLoader中被赋值。若之前LoaderInfo中的Loader已经開始了,那么会新建一个Loader给LoaderInfo作为等待。在这里,假设有等待着的Loader则直接废弃之前的Loader,把等待的Loader扶正。

      相同的。返回的数据也就不进行不论什么处理了。

    4. 假设是新数据。且Loader生命周期正常,则调用callOnLoadFinished对数据进行处理,这里调用了回调函数callback中的onLoadFinished
    5. mInactiveLoaders用来保存不活跃的Loader,假设Loader不活跃了。LoaderManager并不会立即删除它,而是将其保存在mInactiveLoaders中,直到此Loader的新数据来了。就将其销毁掉。

    LoaderManager与生命周期

    Activity和Fragment都拥有getLoaderManager的方法。事实上Fragment的getLoaderManager去获取的就是Activity所管理的众多LoaderManager之中的一个。

    Who标签

    先来看一下Activity的getLoaderManager方法:

    1. LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
    2. if (mAllLoaderManagers == null) {
    3. mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
    4. }
    5. LoaderManagerImpl lm = mAllLoaderManagers.get(who);
    6. if (lm == null) {
    7. if (create) {
    8. lm = new LoaderManagerImpl(who, this, started);
    9. mAllLoaderManagers.put(who, lm);
    10. }
    11. } else {
    12. lm.updateActivity(this);
    13. }
    14. return lm;
    15. }

    mAllLoaderManagers保存着一个Activity所拥有的全部LoaderManager,其key为String类型的who变量。若从Activity调用getLoaderManager,那么所得LoaderManager的who标签为(root):

    1. public LoaderManager getLoaderManager() {
    2. if (mLoaderManager != null) {
    3. return mLoaderManager;
    4. }
    5. mCheckedForLoaderManager = true;
    6. mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
    7. return mLoaderManager;
    8. }

    若从Fragment中使用getLoaderManager。则所得LoaderManager的who标签会依据Fragment的层级不同而不同。在Activity中处于最顶级的Fragment的who标签为:

    1. android:fragment:X

    X为序号。

    而属于Fragment的Fragment所活的的LoaderManager who标签就成为了:

    1. android:fragment:X:Y

    甚至更大的深度。

    LoaderManager状态量mLoadersStarted

    在开篇分析的时候分析过LoaderManager内部保存了一个mStarted状态,非常多操作会依据这个状态做不同处理。

    通过上边的分析也能看出。Fragment中获取LoaderManager终于是通过Activity获取的,在LoaderManager构造时,就将一个状态量mLoadersStarted传递了进去,这个状态量交给LoaderManager自行管理。

    而不管是Fragment还是Activity,都有mLoadersStarted这样一个状态量,在onStart生命周期后为true,onStop后为false。所以在onStart生命周期后做initLoader操作就会使Loader一经初始化就開始执行了。

    Fragment和Activity的生命周期

    Fragment和Activity可以对LoaderManager产生影响的生命周期是一样的。

    onStart

    系统在onStart阶段会获取LoaderManager,假设成功获取,则调用LoaderManager的doStart(),这里须要特别说明的是,尽管全部LoaderManager都是保存在Activity中,可是在Activity的onStart生命周期其也仅仅是会获取属于自己的(root)标签LoaderManager,而并非将全部保存在mAllLoaderManagers里的Manager全部遍历一遍。

    onStop(performStop)

    处于onStop生命周期,可是系统内部是通过performStop调用的。在这里。相同会获取属于自己的LoaderManager。假设Activity是由于配置改变出发的onStop(旋转屏幕),则调用LoaderManager的doRetain()接口,假设不是,就调用LoaderManager的doStop()接口。

    onDestroy(performDestroy)

    调用LoaderManager的doDestroy()接口销毁LoaderManager。

    LoaderManager的生命周期

    由于LoaderManager与Fragment/Activity的生命周期紧密相连,所以想要用好LoaderManager就必须了解其自身的生命周期,这样就能把握数据的完整变化规律了。

    正常的从出生到销毁:

    1. doStart() -> doReportStart() -> doStop() -> doDestroy()

    Activity配置发生变化:

    1. doStart() -> doRetain() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()

    Fragment在onDestroyView()之后还会运行LoaderManager的doReportNextStart(), 即:

    1. doStart() -> doRetain() -> doReportNextStart() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()
    1. doStart()会将LoaderManager中保存的全部Loader都启动。

      终于是执行每个Loader的onStartLoading()方法。

      仅仅要是通过initLoader使用过的Loader都会记录在LoaderManager的mLoaders中。那么问题来了:

      如何在Fragment/Activity不销毁的前提下从LoaderManager中移除某个使用过的Loader呢?

      答案就是使用LoaderManager的接口去除指定ID的Loader:

      1. public void destroyLoader(int id)

      这样就能在mLoaders中移除掉了。下次onStart的时候就没有这个Loader什么事了。

    2. doReportStart()。假设Fragment上一次在销毁并重做,并且数据有效的话会在这里主动上报数据,终于走到callback的onLoadFinished中。

    3. doStop()会停止mLoaders保存的全部Loader。终于是执行每个Loader的onStopLoading()方法。
    4. doDestroy()会清空全部有效和无效Loader。LoaderManager中不再存在不论什么Loader。
    5. doRetain()会将LoaderManager的mRetaining状态置位true。并且保存retain时LoaderInfo的mStarted状态
    6. finishRetain()假设之前所保存的mStarted与如今的不一样并且新的状态是停止的话。就停止掉这个Loader。否则若有数据并且不是要下次再上报(没有call doReportNextStart)的话就上报给callback的onLoadFinished
    7. doReportNextStart()。依据第6条,已经可以理解了。当Fragment运行到onDestroyView生命周期时,对自己的LoaderManager发出请求:即使如今有数据也不要进行上报。等我重做再到onStart生命周期时再给我

    外部接口:restartLoader

    在熟知了LoaderManager的梗概之后最后分析restartLoader就行更好理解了:

    1. public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback)
    1. 若此ID相应的Loader之前使用过。则去除这个Loader,假设这个Loader不在执行中。则销毁它然后又一次创建一个Loader;假设在执行中。则创建一个Loader等待在这个Loader后边
    2. 若此ID的Loader是新的,之前并没有使用过。则创建这个Loader,同initLoader。

  • 相关阅读:
    【YbtOJ#20068】连通子图
    【YbtOJ#20067】糖果分配
    【GMOJ6801】模拟patrick
    【GMOJ6800】模拟spongebob
    【洛谷P4449】于神之怒加强版
    【洛谷P3601】签到题
    【洛谷P2408】不同子串个数
    【洛谷P3809】【模板】后缀排序
    【JZOJ1753】锻炼身体
    【GMOJ1164】求和
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5096751.html
Copyright © 2011-2022 走看看