簡介
在Android異步處理之AsyncTaskLoader簡單使用中我簡單的介紹了一下AsyncTaskLoader
的基本用法和使用場景,對AsyncTaskLoader
還不是很熟悉的小伙伴可以先簡單學習一下。
相信讀過Android異步處理之AsyncTaskLoader簡單使用后,大家對烤面包機,面包師,面包房的例子還是有點印象的,那么接下來趁熱打鐵,繼續沿用這個買面包的例子講述一下AsyncTaskLoader
的設計原理。
設計原理
在講設計原理之前,先簡單了解一下AsyncTaskLoader
的父類Loader
:
A class that performs asynchronous loading of data. While Loaders are active they should monitor the source of their data and deliver new results when the contents change. See LoaderManager for more detail.
簡單理解一下Loader
就是用來異步加載數據的,當Loader
處於活動狀態的時候需要監視數據並且在數據發生改變時加載和分發新的數據。在上述描述中我們還發現了LoaderManager
這個對象,正是因為有了它,Loader
才具有生命力。
下面看一下LoaderManager
的簡單介紹:
Interface associated with an Activity or Fragment for managing one or more Loader instances associated with it. This helps an application manage longer-running operations in conjunction with the Activity or Fragment lifecycle; the most common use of this is with a CursorLoader, however applications are free to write their own loaders for loading other types of data. While the LoaderManager API was introduced in HONEYCOMB, a version of the API at is also available for use on older platforms through FragmentActivity. See the blog post Fragments For All for more details.
簡單理解一下就是說LoaderManager
是配合着Activity
,Fragment
的生命周期來管理Loader
接下來用一張類圖來簡單展示一下Loader,AsyncTaskLoader,AsyncTask,LoaderManager,Activity之間的關系
圖-1 相關類之間的關系
接口
1.OnLoadCompleteListener
被聲明在Loader
中,用於Loader
加載完數據后回調,從上圖可以看出LoaderInfo
實現了這個接口,說明當Loader
完成數據加載后會回調LoaderInfo
的onLoadComplete()
方法。
2.LoaderCallbacks
被聲明在LoaderManager
中,從上圖的LoaderInfo
中可以看到 mCallbacks
這個變量,它便是LoaderCallbacks
的引用,用於當Loader
加載完數據后回調上面提及的onLoadComplete()
,最終回調onLoadFinished()
方法將最新加載的數據傳遞給客戶端。
類
1.Loader
抽象類負責定義相關接口和約束。其變量mListener
就是加載完數據的回調。那具體是如何回調的呢?答案就在deliverResult()
方法中
Loader.java
--------------------------
public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}
再看registerListener()
方法:
Loader.java
--------------------------
public void registerListener(int id, OnLoadCompleteListener<D> listener) {
if (mListener != null) {
throw new IllegalStateException("There is already a listener registered");
}
mListener = listener;
mId = id;
}
外部就是通過調用Loader
的registerListener()
方法將OnLoadCompleteListener
接口注冊進來的。
2.AsyncTaskLoader
繼承自Loader
,其中變量mTask
正是AsyncTask
類型,這里也論證了Android異步處理之AsyncTaskLoader簡單使用中的說法,將AsyncTaskLoader
比作面包師的話AsyncTask
就是烤面包機的說法。AsyncTaskLoader
中就是通過AsyncTask
來完成異步加載數據這個操作的。
3.LoaderInfo
LoaderInfo
其實是對Loader
的一個封裝,它掌握了Loader
一系列的工作狀態如:
LoaderInfo.java
-----------------------------
boolean mHaveData;
boolean mDeliveredData;
Object mData;
boolean mStarted;
boolean mRetaining;
boolean mRetainingStarted;
boolean mReportNextStart;
boolean mDestroyed;
boolean mListenerRegistered;
還有一系列的動作指令:
LoaderInfo.java
-----------------------------
void start() {...}
void retain() {...}
void reportStart() {...}
void stop() {...}
void cancel() {...}
void destroy() {...}
4.LoaderManager和LoaderManagerImpl
LoaderManager
定義了作為Loader
的管理者應該有哪些操作,而LoaderManagerImpl
則具體實現這些操作。如果說把Loader
比作面包師的話,那LoaderManager
就算是面包店的老板吧,廚師什么時候該上班,什么時候該下班都由他管。
其中mLoaders
變量為一個數組,用於保存 多個Loader
這也說明了一個面包店可以有多個面包師負責制作不同類型的面包如:
這么多種類的面包如果讓一個面包師來做我看他也會累的夠嗆。
運行流程梳理
在接下來的幾步中有任何的疑惑都可以回過頭看看【圖1】。
1
那么了解上述這些類是干嘛的以后我們就來看看當這些個類運行起來是一個怎樣的流程吧。
我們還是接着Android異步處理之AsyncTaskLoader簡單使用中的例子來講。一切起源起於onCreate()
(至少對於APP開發來說是這樣),那就從MainActivity
的onCreate()
來看起吧。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//這里假設面包房剛開門的時候已經有9個人在排隊了。
mNeededBreads = 9;
mBaker = new Baker(this, mBreadCallback);
mBakery = new Bakery(mBaker);
//1.實現`LoaderCallbacks`接口。
mCallbacks = new LoaderCallbacks<List<Bread>>() {
@Override
public Loader<List<Bread>> onCreateLoader(int id, Bundle args) {
if (mBaker == null) {
mBaker = new Baker(MainActivity.this, mBreadCallback);
}
return mBaker;
}
@Override
public void onLoadFinished(Loader<List<Bread>> loader, List<Bread> data) {
mNeededBreads = 0 ;
Log.d("scott", "sell " + data.size() + " breads") ;
}
@Override
public void onLoaderReset(Loader<List<Bread>> loader) {
}
};
//2.在`LoaderManager`中注冊這個接口。
getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
//3.模擬源源不斷的顧客
mockCustomer();
}
這一步主要做了三件事情:
1.實現LoaderCallbacks
接口。
2.在LoaderManager
中注冊這個接口。
3.模擬源源不斷的顧客
那么這里的mCallbacks充當了什么角色呢?其實它應該相當於一個面包師Loader
和面包房店長LoaderManager
的中間橋梁。當店長需要面包師的時候就會調用onCreateLoader()
來獲得一個面包師。同樣當面包師完成面包的烤制工作后就會調用onLoadFinished()
來告訴店長面包做好了。但實際情況應該不會如此,面包做好了服務員應該會直接將面包傳遞給顧客。
2
接下來我們看一下restartLoader()
這個方法:
LoaderManager.java
--------------------------------------------------------------
public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
LoaderInfo info = mLoaders.get(id);
//...省略部分代碼
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
return (Loader<D>)info.mLoader;
}
這里直接調用了createAndInstallLoader()
方法來生成一個LoaderInfo
對象。接着看createAndInstallLoader()
方法:
LoaderManager.java
--------------------------------------------------------------
private LoaderInfo createAndInstallLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
try {
mCreatingLoader = true;
//1.創建LoaderInfo對象
LoaderInfo info = createLoader(id, args, callback);
//2.安裝LoaderInfo對象
installLoader(info);
return info;
} finally {
mCreatingLoader = false;
}
}
3
這里分兩步來看:
1.創建LoaderInfo
對象
LoaderInfo.java
------------------------------------------------------------
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
//實例化LoaderInfo,並將id,args,callback賦值給mId,mArgs,mCallbacks
LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
//這里的callback就是上面onCreate中的mCallbacks
//獲得Loader實例Baker
Loader<Object> loader = callback.onCreateLoader(id, args);
//將Baker賦值給info中的mLoader字段
info.mLoader = (Loader<Object>)loader;
return info;
}
2.安裝LoaderInfo對象
LoaderInfo.java
------------------------------------------------------------
void installLoader(LoaderInfo info) {
//將info放入mLoaders數組
mLoaders.put(info.mId, info);
//這一步mStarted=false,不會走下面的if條件語句,那么到這里一切都結束了?
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();
}
}
4
到這里其實我們已經不能在往下跟代碼了,因為此時的mStarted=false
,也就是說不會走info.start()
這個方法。那么數據是在什么時候被加載的呢?冷靜看上面的這段英文注釋,Activity
會在它的onStart()
方法中啟動所有已經存在的Loader
,真是山窮水盡疑無路,柳暗花明又一村
。我們就去onStart()
中看個究竟。
Activity.java
------------------------------------------------------
protected void onStart() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
mCalled = true;
if (!mLoadersStarted) {
mLoadersStarted = true;
if (mLoaderManager != null) {
//看這里o(^▽^)o
mLoaderManager.doStart();
} else if (!mCheckedForLoaderManager) {
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
}
mCheckedForLoaderManager = true;
}
getApplication().dispatchActivityStarted(this);
}
繼續LoaderManager
的doStart()
方法:
LoaderManager.java
--------------------------------------------------------------
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
//看,在這里LoaderInfo被啟動了
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).start();
}
}
5
下面轉移戰場進入LoaderInfo
看看
LoaderInfo.java
--------------------------------------------
void start() {
//...
mStarted = true;
if (mLoader == null && mCallbacks != null) {
mLoader = mCallbacks.onCreateLoader(mId, mArgs);
}
if (mLoader != null) {
//...
if (!mListenerRegistered) {
//將OnLoadCompleteListener接口注冊給Loader
mLoader.registerListener(mId, this);
//將OnLoadCanceledListener接口注冊給Loader
mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
//開始加載,實質性的一步。
mLoader.startLoading();
}
}
6
進入Loader
的startLoading()
方法看看:
Loader.java
--------------------------------------------
public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}
7
接着看onStartLoading()
:
Loader.java
--------------------------------------------
/**
* 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() {
}
注釋寫的很清楚,子類必須要覆蓋這個方法,接着我們看看我們久違的Baker
(比忘了Baker可是Loader的子類啊)吧:
Baker.java
--------------------------------------------
@Override
protected void onStartLoading() {
//這個可以解釋為強行加載,太暴力了。
forceLoad();
}
8
那么forceLoad()
又是哪里的呢?
Loader.java
--------------------------------------------
/**
* Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
* loaded data set and load a new one. This simply calls through to the
* implementation's {@link #onForceLoad()}. You generally should only call this
* when the loader is started -- that is, {@link #isStarted()} returns true.
*
* <p>Must be called from the process's main thread.
*/
public void forceLoad() {
onForceLoad();
}
接着看onForceLoad()
:
Loader.java
--------------------------------------------
/**
* Subclasses must implement this to take care of requests to {@link #forceLoad()}.
* This will always be called from the process's main thread.
*/
protected void onForceLoad() {
}
9
又來這套。。。服了Google的工程師了。接着在AsyncTaskLoader
中找到了onForceLoad()
:
AsyncTaskLoader.java
--------------------------------------------
@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
//這里的LoadTask繼承自AsyncTask
mTask = new LoadTask();
if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
//執行准備就緒的mTask
executePendingTask();
}
接着看executePendingTask()
:
AsyncTaskLoader.java
--------------------------------------------
void executePendingTask() {
if (mCancellingTask == null && mTask != null) {
//...
//...
//到這里mTask就真正被執行了,即烤面包機考試工作了。
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}
10
接下來我們來看一下mTask
對應的類LoadTask
的定義吧。
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) Log.v(TAG, this + " >>> doInBackground");
try {
D data = AsyncTaskLoader.this.onLoadInBackground();
if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
return data;
} catch (OperationCanceledException ex) {
//...
if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex);
return null;
}
}
/* 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();
}
}
/* Runs on the UI thread */
@Override
protected void onCancelled(D data) {
//...
}
/* Runs on the UI thread, when the waiting task is posted to a handler.
* This method is only executed when task execution was deferred (waiting was true). */
@Override
public void run() {
//...
}
/* Used for testing purposes to wait for the task to complete. */
public void waitForLoader() {
//...
}
}
11
這里有兩個方法需要關注:
1.doInBackground()
方法
用過AsyncTask
的應該都了解,異步操作都是放在這里執行的,我們看一下都做了什么操作?
D data = AsyncTaskLoader.this.onLoadInBackground();
接着看onLoadInBackground
:
protected D onLoadInBackground() {
return loadInBackground();
}
這個loadInBackground()
是不是有點熟悉了?沒錯這就是我們在Baker
中重寫的方法:
Baker.java
------------------------------------------------------
@Override
public List<Bread> loadInBackground() {
List<Bread> breads = new ArrayList<Bread>();
int needs = mCallback.getNeededBreads();
for (int i = 0; i < needs; i++) {
breads.add(new Bread());
}
return breads;
}
OK,到這里面包已經烤完(耗時操作),接着就看這些香噴噴的面包怎么到顧客的手里的吧?
12
2.onPostExecute()
方法
LoadTask.java
-------------------------------------------------
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}
走的是dispatchOnLoadComplete()
方法:
AsyncTaskLoader.java
------------------------------------------------------
void dispatchOnLoadComplete(LoadTask task, D data) {
if (mTask != task) {
if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
//容錯處理
dispatchOnCancelled(task, data);
} else {
if (isAbandoned()) {
// This cursor has been abandoned; just cancel the new data.
onCanceled(data);
} else {
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask = null;
if (DEBUG) Log.v(TAG, "Delivering result");
//重點在這里
deliverResult(data);
}
}
}
13
繼續往下走 deliverResult(data)
:
Loader.java
------------------------------------------------------
public void deliverResult(D data) {
if (mListener != null) {
//這里的mListener就是之前在【5】中注冊的OnLoadCompleteListener接口
mListener.onLoadComplete(this, data);
}
}
14
那么自然又要轉移到LoaderInfo
中的onLoadComplete()
中去了:
LoaderInfo.java
------------------------------------------------------
@Override
public void onLoadComplete(Loader<Object> loader, Object data) {
//...
if (mLoaders.get(mId) != this) {
// This data is not coming from the current active loader.
// We don't care about it.
if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active");
return;
}
LoaderInfo pending = mPendingLoader;
if (pending != null) {
// There is a new request pending and we were just
// waiting for the old one to complete before starting
// it. So now it is time, switch over to the new loader.
//...
return;
}
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
//重點看這里
callOnLoadFinished(loader, data);
}
}
//if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this);
// We have now given the application the new loader with its
// loaded data, so it should have stopped using the previous
// loader. If there is a previous loader on the inactive list,
// clean it up.
//...
}
15
繼續看callOnLoadFinished()
:
LoaderInfo.java
------------------------------------------------------
void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
//...
try {
if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
//這里是重點了
mCallbacks.onLoadFinished(loader, data);
} finally {
//...
}
mDeliveredData = true;
}
mCallbacks
又是什么呢?在第【3】步中的:
//實例化LoaderInfo,並將id,args,callback賦值給mId,mArgs,mCallbacks
LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
而這里的callback就是我們在第【1】步中定義的mCallbacks 對象。
饒了這么大一圈,最后還是走到了第【1】步中的:
@Override
public void onLoadFinished(Loader<List<Bread>> loader, List<Bread> data) {
mNeededBreads = 0 ;
//此時面包以成功送至顧客手中(相當於將數據更新在UI上,這里是main線程大家大可放心使用這些數據)
Log.d("scott", "sell " + data.size() + " breads") ;
}
那么到此為止這個流程就走完了。
總結
1.Loader可以配合中Activity或Fragment的生命周期來加載數據。
2.讀源碼的時候畫類關系圖很重要!讀源碼的時候畫類關系圖很重要!讀源碼的時候畫類關系圖很重要!
3.文章寫的倉促,如果有有問題的地方歡迎指出。