天天看点

LoaderManager&Loader源码剖析(2) – LoaderManager对Loader的主要管理

 阅读本文前,请阅读上一篇文章《LoaderManager&Loader源码剖析(1) – Activity对LoaderManager的管理》。

我们在使用LoaderManager时,通常做的第一件事就是:

// Prepare the loader.  Either re-connect with an existing one,
            // or start a new one.
            getLoaderManager().initLoader(0, null, this);
           

所以我们先从getLoaderManager()开始吧。根据上篇的文章介绍,我们知道,最终会调用到:

class LoaderManagerImpl extends LoaderManager {
……
final String mWho;
    Activity mActivity;
boolean mStarted;
 LoaderManagerImpl(String who, Activity activity, boolean started) {
        mWho = who;
        mActivity = activity;
        mStarted = started;
}
……
           

mWho:表明LoaderManager的身份,在这里传入的参数值是”(root)”。

mActivity: Activity引用。属于哪个Activity。

mStarted:是否启动。这个值由Activity负责传递的。从上篇文章介绍我们知道:

public LoaderManager getLoaderManager() {
        if (mLoaderManager != null) {
            return mLoaderManager;
        }
        mCheckedForLoaderManager = true;
        mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
        return mLoaderManager;
    }
    
    LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
        if (mAllLoaderManagers == null) {
            mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
        }
        LoaderManagerImpl lm = mAllLoaderManagers.get(who);
        if (lm == null) {
            if (create) {
                lm = new LoaderManagerImpl(who, this, started);
                mAllLoaderManagers.put(who, lm);
            }
        } else {
            lm.updateActivity(this);
        }
        return lm;
    }
           

LoaderManagerImpl的第3个参数started实际上是由如下代码中的mLoadersStarted传递的:

mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);

mLoadersStarted而在Activity中的onStart()赋值为true的。这恰恰也说明了mLoadersStarted有一个重要作用,那就是表明Activity实际上已经进入started状态,此时应该启动所有的Loader了。

Activity创建完LoaderManager以后,我们通常会接着调用LoaderManager. initLoader,那么我们接着来看一下initLoader方法:

@SuppressWarnings("unchecked")
    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;
}
           

mCreatingLoader是一个保护型变量,避免在callback中重复循环调用initLoader方法。我们第一次创建id值的Loader时,需要调用createAndInstallLoader创建一个LoaderInfo实例来代表ID为id的Loader,否则只是简单更新一下mCallbacks引用。

至于createAndInstallLoader,请看代码:

private LoaderInfo createAndInstallLoader(int id, Bundle args,
            LoaderManager.LoaderCallbacks<Object> callback) {
        try {
            ……
            LoaderInfo info = createLoader(id, args, callback);
            installLoader(info);
            ……
        } finally {
            ……
        }
    }
           

上面做了两件事:1)createLoader:创建一个Loader,当然代表这个Loader实例的是一个内部类LoaderInfo。2)installLoader:安装这个Loader。

接着看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;
    }
           

做了2件事:

1) 创建一个LoaderInfo, Loader id,参数args,还有callback都保存了起来;

2) 调用callback的onCreateLoader,将用户创建的Loader实例引用保存到info.mLoader成员中;

再看installLoader:

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();
        }
    }
           

原来调用了刚才创建的LoaderInfo的start方法来启动loader啊:

void start() {            
            ……
                if (!mListenerRegistered) {
                    mLoader.registerListener(mId, this);
                    mLoader.registerOnLoadCanceledListener(this);
                    mListenerRegistered = true;
                }
                mLoader.startLoading();
            }
        }
           

上面做了2件事:

1)       注册监听接口:registerListener注册了接口onLoadComplete(),registerOnLoadCanceledListener注册了接口onLoadCanceled(),这两个方法有什么用呢?我们暂不去分析源码,否则纵深分析,最后必定毫无头绪,目前只分析LoaderManager吧。那么我们先看看Loader的SDK描述即可:

public void registerListener (int id, OnLoadCompleteListener<D> listener)

Added in API level 11

注册一个接口类,当加载完成时,接收回调。这个回调发生在主线程中,所以直接将结果传递给视图组件是安全的。

必须在主线程中调用。

publicvoid registerOnLoadCanceledListener (OnLoadCanceledListener<D> listener)

Added in API level 16

注册一个接口类,当加载取消时,接收回调。这个回调发生在主线程中,所以直接将结果传递给视图组件是安全的。

必须在主线程中调用。

参数
listener 注册的监听类.

2)       mLoader.startLoading:启动Loader,开始后台数据的加载。我们看看SDK是怎么解释这个方法调用的:

public final void startLoading ()

Added in API level 11

当关联的fragment/activity启动时,这个函数会被LoaderManager自动调用。如果你使用了LoaderManager来管理Loader的话,请千万不要自己直接调用这个方法,否则的话,你会破坏Loader的管理。开始异步加载Loader数据。当结果数据准备好的时候,会在主线程中调用相关回调。如果之前的加载已经完成,并且结果数据依然有效的话,那么结果会被直接传给回调方法。这个Loader会监听数据源的变化,如果数据源发生变化,它会调用回调来通知变化。调用 stopLoading() 会停止回调传送。

本函数会首先更新Loader的内部状态,这样函数isStarted()和isReset()就能够返回正确的状态值。接着会调用onStartLoading()实现。

必须在主线程中调用。

再看LoaderManager上面的initLoader方法:

if (info.mHaveData && mStarted) {
            // If the loader has already generated its data, report it now.
            info.callOnLoadFinished(info.mLoader, info.mData);
        }
           

如果当前的Loader已经加载完数据了,并且已经是开始状态,那么会调用callOnLoadFinished(),其中调用了用户的回调onLoadFinished()回调方法,通知用户加载完成。

当然了,第一次启动Loader,数据肯定还未加载,那么我们在Loader加载完成后,会收到回调LoaderInfo类的 onLoadComplete方法,这个方法是在上面的LoaderInfo.start()方法中注册的。那么onLoadComplete做了哪些事情呢:

@Override
        public void onLoadComplete(Loader<Object> loader, Object data) {
            ……
            
            LoaderInfo pending = mPendingLoader;
            if (pending != null) {
                //(1)我们在启动新的Loader的时候,可能一些旧的Loader正在加载,此时我们等待它们完成,然后直接销毁即可,然后再启动新的Loader.
                if (DEBUG) Log.v(TAG, "  Switching to pending loader: " + pending);
                mPendingLoader = null;
                mLoaders.put(mId, null);
                destroy();
                installLoader(pending);
                return;
            }
            
            // (2)通知用户加载完成,并把新数据传递给用户,如果你启动了多个loader,可以使用loader中的getId()方法来获得ID进行区分。当然了,传递的判断条件是两个,第1就是新数据与旧数据不同,或者第2还未传递过数据,即mHaveData = false。
            if (mData != data || !mHaveData) {
                mData = data;
                mHaveData = true;
                if (mStarted) {
                    callOnLoadFinished(loader, data);
                }
            }

            //if (DEBUG) Log.v(TAG, "  onLoadFinished returned: " + this);

            // (3)如果你又调用了LoaderManager.restartLoader()的话,那么那些已经加载完成的相同ID的Loader就会进入不活动状态,此时也需要调用它们的destory()进行销毁。
            LoaderInfo info = mInactiveLoaders.get(mId);
            if (info != null && info != this) {
                info.mDeliveredData = false;
                info.destroy();
                mInactiveLoaders.remove(mId);
            }

           ……
        }
           

请看上面代码中标注的(1),(2),(3),这些都是某个Loader加载完毕时需要做的3件事。请注意它们的判断条件。其中涉及另外两个LoaderInfo中的方法callOnLoadFinished()和destroy()。一个是通知用户新数据的加载完成,一个是销毁旧的或无用的数据。

我们先来看看callOnLoadFinished(),代码如下:

void callOnLoadFinished(Loader<Object> loader, Object data) {
            …
//通知用户回调,传递加载的新数据
      mCallbacks.onLoadFinished(loader, data);
               … 
}
           

再看一下LoaderInfo.destroy():

void destroy() {
            ……
                try {
//(1)通知用户回调,Loader被重置了,此时mLoader相关的数据都不可用了,应尽快解绑定此Loader。
                    mCallbacks.onLoaderReset(mLoader);
                } finally {
                    if (mActivity != null) {
                        mActivity.mFragments.mNoTransactionsBecause = lastBecause;
                    }
                }
            }
            ……
//(2)通知调用Loader的reset()方法,重置Loader。
                mLoader.reset();
            ……
            if (mPendingLoader != null) {
                mPendingLoader.destroy();
            }
        }
           

上面主要做了2件事,1是通知用户解除绑定旧Loader,一个是通知Loader重置数据,比如CursorLoader就会关闭游标。

Loader的reset()方法我们暂不做研究。但是可以查看一下SDK描述:

public void reset ()

Added in API level 11

此方法会更新Loader内部状态,这样它的isStarted()和isReset()方法就能返回正确的值。接下来会调用Loader实现类的onReset()方法。

此方法必须在UI主线程中调用。

原来reset会先设置状态,然后会调用Loader实现类的onReset()方法。比如CursorLoader的onReset()此时就会被调用,或者你自定义Loader的onReset()方法。

通常情况下,用户经常会调用LoaderManager.restartLoader()方法,比如根据用户输入的文本进行某种数据的查找,如果用户的查找文本发生变化的话,那么用户就会调用restartLoader()来进行重新查找的。

public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
        ……
        //(1)我们首先需要先看一看是否已经创建过此id的Loader。
        LoaderInfo info = mLoaders.get(id);
        if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args);
        if (info != null) {
            LoaderInfo inactive = mInactiveLoaders.get(id);
		    //查看是否有同id的旧Loader存在
            if (inactive != null) {
                if (info.mHaveData) {
                    // This loader now has data...  we are probably being
                    // called from within onLoadComplete, where we haven't
                    // yet destroyed the last inactive loader.  So just do
                    // that now.
                    if (DEBUG) Log.v(TAG, "  Removing last inactive loader: " + info);
				   //(2)销毁那些旧Loader。
                    inactive.mDeliveredData = false;
                    inactive.destroy();
				   //舍弃当前使用的Loader,这样此Loader就不会再向用户传送数据了。
				   //因为此时重启了该Loader,旧数据无需传递。
                    info.mLoader.abandon();
                    mInactiveLoaders.put(id, info);
                } else {
                    // We already have an inactive loader for this ID that we are
                    // waiting for!  What to do, what to do...
                    if (!info.mStarted) {
                        //(3)如果此id的Loader尚未启动,那么只需销毁即可。
                        if (DEBUG) Log.v(TAG, "  Current loader is stopped; replacing");
                        mLoaders.put(id, null);
                        info.destroy();
                    } else {
                        // (4)此时,说明一个同id的Loader正在加载数据,但是尚未加载完成,此时我们调用它的cancel()通知退出加载。
                        if (DEBUG) Log.v(TAG, "  Current loader is running; attempting to cancel");
                        info.cancel();
                        if (info.mPendingLoader != null) {
                            if (DEBUG) Log.v(TAG, "  Removing pending loader: " + info.mPendingLoader);
                            info.mPendingLoader.destroy();
                            info.mPendingLoader = null;
                        }
                        if (DEBUG) Log.v(TAG, "  Enqueuing as new pending loader");
					   //创建一个此id值的Loader,并赋给mPendingLoader,也许你会问为什么不直接启动它呢,
//别忘了,此时已经有一个Loader在加载数据了,而且我们已经调用它的cancel来通知它退出加载,当然,通常情况下,
//这个Loader也许刚好加载完成,那么会收到onLoadComplete()回调,也许正常退出加载,此时收到回调onLoadCanceled(),
//所以在这两个回调里,我们再启动mPendingLoader,不信的话,参考一下这两个方法,就能找到installLoader(pending)。
                        info.mPendingLoader = createLoader(id, args, 
                                (LoaderManager.LoaderCallbacks<Object>)callback);
                        return (Loader<D>)info.mPendingLoader.mLoader;
                    }
                }
            } else {
                // (5)通知Loader的舍弃数据,因为此时数据应该无效了。另外别忘了保存这个旧Loader,这样当新数据到来的时候,就可以销毁旧数据了。
                if (DEBUG) Log.v(TAG, "  Making last loader inactive: " + info);
                info.mLoader.abandon();
                mInactiveLoaders.put(id, info);
            }
        }
        
	    //(6)如果当前还未创建过Loader,那么就是简单地创建并启动一个id值的Loader即可。
        info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
        return (Loader<D>)info.mLoader;
    }
           

上面总共做了6件事。我们看看LoaderInfo的cancel()方法做了什么事情呢。

void cancel() {
            if (DEBUG) Log.v(TAG, "  Canceling: " + this);
            if (mStarted && mLoader != null && mListenerRegistered) {
                //调用Loader.cancelLoad()来停止数据加载。返回值为false时,说明此Loader已经加载完数据了,或者不能立即终止,那么我们直接调用onLoadCanceled()方法。
                if (!mLoader.cancelLoad()) {
                    onLoadCanceled(mLoader);
                }
            }
        }
           

另外我们还需要看一下Loader的abandon()做了什么事情:

public void abandon ()

Added in API level 11

在reset()方法之前调用,通知Loader保存当前状态,并且不再传递任何新数据给用户。