您的位置:首页 > 移动开发 > Android开发

Android Loader的机制以及源码分析

2017-06-14 19:01 387 查看
刚刚开始的时候我也不知道有Loader这个东西,但是无意之间发现了网上关于Loader的描述,说这个是加载和网络请求的神器,好吧我孤陋寡闻了,但是知道了就停不下看源码的欲望,那就read the fucking source code.

使用:

Loader有两个实现类,一个是AsyncTaskLoader(抽象类)和CurdorLoader,这两个听名字就知道是干什么的我就不瞎比比了。

还是从抽象的AsyncTaskLoader来说起吧,想要使用这个类需要实现三个方法,

onStartLoading() 这个方法里需要重写在里边写forceLoad()以及流程控制需要在加载前写的,刚开始我写的时候就没加,懵X了

loadInBackground()这个方法里边执行的就是一些耗时的操作

deliverResult() 这个就是传递最后得到的结果值,我也就不赘述了

* 源码分析: *

- LoaderManagerImpl:

为什么从LoaderManagerImpl分析,看到最后你就知道了,客官不急。。

1.initLoader:

①:LoaderInfo info = mLoaders.get(id);//获取映射表中对应的loaderInfo;

②:判断Loader是不是为Null

info==null: info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks)callback);

//创建LoaderInfo并且执行Load方法:

info!=null info.mCallbacks = (LoaderManager.LoaderCallbacks)callback;

//复用Loader并且重新设置callback;

③:if (info.mHaveData && mStarted) {

// 如果已经执行过load方法,直接回调

info.callOnLoadFinished(info.mLoader, info.mData);

}

④:返回loader

2.分析createAndInstallLoader

上边的判断LoaderInfo的方法之后咱们直接看第一次创建,这样来看整个流程

createAndInstallLoader:

①:mCreatingLoader = true;

//打标机值,这个值会在initLoader中用到判断抛异常

②:LoaderInfo info = createLoader(id, args, callback);

//这个地方时用来创建LoaderInfo(LoaderManagerImpl的一个内部类)

③:installLoader(info);

//这个是这个Loader流程开始的地方下边会详细分析这个方法

④:mCreatingLoader = false;

//将标记值置回正常

3分析InstallLoader方法:

这个地方应该说是Loader的执行流程了,算是一个小小的重点了。

①:mLoaders.put(info.mId, info);

//这个地方时put到映射表,和上边从映射表中取是一个对应的关系

②: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();

}

这个地方看上边的英文的注释应该很明白,就是在activity的onStart()方法里边会执行所有存在的Loader。

4.分析info.start()

这个地方就是开始执行Loader生命周期的操作了。

①:前边会做一系列的判断,是否这个已经started过了,如果started过了那就直接返回了,根据生命周期判断最后肯定是执行finish那个方法。

②:if (mLoader == null && mCallbacks != null) {

mLoader = mCallbacks.onCreateLoader(mId, mArgs);

}

这个地方时调用LoaderCallback的onCreateLoader方法,这个应该不用过多的纠结就能看明白相当简单吧

③: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();

}

这个方法需要好好的强调一下,刚刚开始用AsyncTaskLoader的时候我就犯了没有重写startLoading的方法(不过也是,这也是逼迫我吭哧吭哧看源码的原因,累觉不爱啊,To Young To Simple)。上边注册观察者模式的监听我就不说了,我默认大家都知道,毕竟能想来看源码的都是已经被虐过千百遍的老司机。。。。。

从这个方法强调两点:

a:如果你写的Loader是内部类,那么不能是静态的,不要告诉我你没看见那个异常!

b:必需重写onStartLoading方法,必须!必须!必须!(重要的事情说三遍。。。)如果你还不信那好咱么继续

5.分析mLoader.startLoading()

public final void startLoading() {

mStarted = true;

mReset = false;

mAbandoned = false;

onStartLoading();

}

这会你总该相信了吧,AsyncTaskLoader默认调用的是空的onStartLoading(){},你不要问我怎么知道的!我还是贴出来这个方法吧,Loader中是这样写的

/**

* Subclasses must implement this to take care of loading their data,

* as per {@link #startLoading()}. This is not called by clients directly,

* but as a result of a call to {@link #startLoading()}.

*/

protected void onStartLoading() {

}

英文我就不翻译了,反正我的脸已经肿了。

6.继续是什么?

似乎这一步源码就结束了,可是咱们看看CursorLoader这个完整的实现类就知道了,差的还远呢!

protected void onStartLoading() {

if (mCursor != null) {

deliverResult(mCursor);

}

if (takeContentChanged() || mCursor == null) {

forceLoad();

}

}

上边这个代码有两个分支。一个是传递结果,一个是强制执行,咱么分别来看

a:如果cursor!=null就说明执行过了,直接跳过执行doInBackgroundLoad了,直接deliverResult();咱们看看deliverResult()的代码,正好上边步骤4中注册的几个监听也能用到了,不废话上码子。

/**

*把load的数据发送给监听,并且只能在Loader的子类中调用,并且只能够在主线程中调用。

* @param data the result of the load

*/

public void deliverResult(D data) {

if (mListener != null) {

mListener.onLoadComplete(this, data);

}

}

监听哪里注册的看看步骤4那里的几行代码,发现监听注册的是LoaderInfo,咱们回去看看它做了什么,新旧LoaderInfo的判断那段代码我就贴了,代码太长容易看眼花,我直接上最本质的代码:

if (mData != data || !mHaveData) {

mData = data;

mHaveData = true;

if (mStarted) {

callOnLoadFinished(loader, data);

}

}

这个方法直接调用了Loader的onLoadFinish(),这个就结束了,我们看下边的一个分支

b: 执行异步的操作

public void forceLoad() {
onForceLoad();
}


看到forceLoad()就是执行了onForceLoad(),然后就看onForceLoad()我感觉这个代码不用看就知道肯定调用了doInBackgroundLoad(),不行

@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
}


先回去判断是不是调用了cancel方法,如果调用了后边对相应的标记值进行判断也可能移除handler中的任务,有兴趣自己看,反正就是跟AsyncTask里边的Cancel很相似,都跟FuturTask有一腿

mTask = new LoadTask();

LoadTask extends AsyncTask<Void, Void, D> implements Runnable


(这个你肯定明白task是啥了吧)思考一下前边的cancel和这新的task的前后关系,有助于你理解onCancelLoad()

if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
executePendingTask();
}


哎呀没有,翻车了?肯定不是,刚刚前边执行的都是在UIThread,loadInBackground明显是在子线程,所以这里肯定有不为人知的线程转换。继续看executePendingTask();

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) Log.v(TAG, "Waiting until "
+ (mLastLoadCompleteTime+mUpdateThrottle)
+ " to execute: " + mTask);
mTask.waiting = true;
mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
return;
}
}
if (DEBUG) Log.v(TAG, "Executing: " + mTask);
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}


反正上边的mUpdateThrottle我自己的理解是过期的时间,不知道你们看了有什么想法,然后mhandler.post和mTask.executeOnExecutor去看看Asynctask的源码就知道肯定是执行线程池和Future,在子线程里边执行doInBackground(),咱们看一下AsyncTaskLoader里边的LoadTask的doInBackground();

/* Runs on a worker thread */
@Override
protected D doInBackground(Void... params) {
if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
try {
D data = AsyncTaskLoader.this.onLoadInBackground();
if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
return data;
} catch (OperationCanceledException ex) {
if (!isCancelled()) {
// onLoadInBackground threw a canceled exception spuriously.
// This is problematic because it means that the LoaderManager did not
// cancel the Loader itself and still expects to receive a result.
// Additionally, the Loader's own state will not have been updated to
// reflect the fact that the task was being canceled.
// So we treat this case as an unhandled exception.
throw ex;
}
if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
return null;
}
}


注释很清楚,反正调用了AsyncTaskLoader.this.onLoadInBackground,然后返回值肯定传到了onPostExecute里边去了,然后看onPostExecute()

/* Runs on the UI thread */
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}


这里执行了AsyncTaskLoader.this.dispatchOnLoadComplete,不用想你们也猜到了就是就是调用了contentChanged进行通知 ,deliverResult用来传递结果,之后的步骤我在上边的a里边介绍了,就不继续往下写了。

与Activity和Fragment生命周期的关联

上边解释了半天AsyncTaskLoader的机制以及流程,是不是感觉你妹的我想知道的是怎么跟activity和fragment关联上的,你讲前边那么多有什么用,我其实想说,其实看源码就是这样,先抓住源码看一条线索,把流程整清楚了再去看更深层次的,现在我来满足你们后边的需求,

吭哧吭哧,调到开始getLoaderManager.

getLoaderManager()

1.它的调用是Activity类里边的

public LoaderManager getLoaderManager() {
return mFragments.getLoaderManager();
}


2.mFragments是什么?

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());


3.然后它的getLoaderManager()做了什么?首先咱么需要跑到FragmentHostCallback里边看他的这个重载的方法。

LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
if (mAllLoaderManagers == null) {
mAllLoaderManagers = new ArrayMap<String, LoaderManager>();
}
LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
if (lm == null) {
if (create) {
lm = new LoaderManagerImpl(who, this, started);
mAllLoaderManagers.put(who, lm);
}
} else {
lm.updateHostController(this);
}
return lm;
}


看他的返回值类型LoaderManagerImpl,然后你知道咱么分析源码的时候为什么找的是LoaderManagerImpl了么,对他就是LoaderManager的实现类,老司机看名字就知道。反正LoaderManagerImpl创建了,然后看看在这个类里边mLoaderManager都做了什么你就清楚了他回调了什么方法结合前边的身边周期就全部清楚了。还是不明白你在看看

FragmentController类里边mHost.mLoaderManager

都做了什么,里边的代码太长了,我粘贴过来可读性不强,不如提醒一下,你自己去看然后结合我刚刚说的那个执行过程,我相信你会豁然开朗。那么

Activity生命周期呢

我去你不说我还真忘了,你去看看Activity这个类里边mFragments这个参数的方法都在Activity的什么方法里边调用了,我相信你会懂的。什么performDestroy()方法啦,看了你就知道了,太长了,我不粘贴了,关键我不想把博客写太长。。。。

后边写的不太详细,怎么说呢,因为系统的代码太长了,都找出来相当麻烦,可读性肯定特别差,没办法只好点一下关键性的地方然后自己去看收获会更大。这个是我自己的看法,如果有问题的话希望能够指出来,让大家一起进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  源码 android loader