一、概述
前面我們比較關注的是LoaderManager
的原理和使用,如果我們只是需要通過LoaderManager
來讀取ContentProvider
封裝的資料,那麼採用系統已經定義好的CursorLoader
就可以了,但是有的時候,我們的資料可能不是用ContentProvider
封裝的,那麼這時候就要學會如何定義自己的Loader
。
二、Loader<D>
類
對於LoaderManager
來說,它所看到的只是一個Loader
類,並呼叫它對應的方法,這些方法分為三類:
- 狀態標誌:
isXXX
- 呼叫介面:
startLoading/stopLoading/reset/cancelLoad/forceLoad/onReset
- 呼叫介面之後的回撥:
onXXX
public class Loader<D> {
public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}
public void deliverCancellation() {
if (mOnLoadCanceledListener != null) {
mOnLoadCanceledListener.onLoadCanceled(this);
}
}
//在startLoading執行完,且沒有呼叫stopLoading或reset。
public boolean isStarted() {
return mStarted;
}
//在該狀態下,不應該上報新的資料,並且應該保持著最後一次上報的資料直到被reset。
public boolean isAbandoned() {
return mAbandoned;
}
//沒有啟動,或者呼叫了reset方法。
public boolean isReset() {
return mReset;
}
<!-- 開始任務相關 -->
//開始任務。
public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}
//開始任務後回撥該方法,呼叫者重寫該方法來載入資料。
protected void onStartLoading() {}
<!-- 取消任務相關 -->
//取消任務。
//1.返回 false 表示不能取消,有可能是已經完成,或者 startLoading 還沒有被呼叫。
//2.這不是一個立刻的過程,因為載入是在後臺執行緒中執行的。
public boolean cancelLoad() {
return onCancelLoad();
}
//呼叫者重寫該方法來做取消後的操作。
protected boolean onCancelLoad() {
return false;
}
<!-- 強制重新整理資料相關 -->
public void forceLoad() {
onForceLoad();
}
protected void onForceLoad() {}
<!-- 停止任務相關 -->
public void stopLoading() {
mStarted = false;
onStopLoading();
}
protected void onStopLoading() {}
<!-- 廢棄任務相關 -->
public void abandon() {
mAbandoned = true;
onAbandon();
}
protected void onAbandon() {}
public void reset() {
onReset();
mReset = true;
mStarted = false;
mAbandoned = false;
mContentChanged = false;
mProcessingChange = false;
}
protected void onReset() {}
//得到mContentChanged的值,並把mContentChanged設為false
public boolean takeContentChanged() {
boolean res = mContentChanged;
mContentChanged = false;
mProcessingChange |= res;
return res;
}
//表明正在處理變化。
public void commitContentChanged() {
mProcessingChange = false;
}
//如果正在處理變化,那麼停止它,並且把mContentChanged設為true。
public void rollbackContentChanged() {
if (mProcessingChange) {
mContentChanged = true;
}
}
//如果當前是start狀態,那麼收到變化的通知就立即重新載入,否則記錄下這個標誌mContentChanged。
public void onContentChanged() {
if (mStarted) {
forceLoad();
} else {
mContentChanged = true;
}
}
}
複製程式碼
可以看到,在Loader<D>
中,提供的是給LoaderManager
呼叫的介面,但是它並沒有真正地執行操作,而是在startLoading
等方法之後,提供了onStartLoading
等回撥,讓子類在這些回撥中去執行對應的操作。
三、AsyncTaskLoader
對於Loader
的使用者來說,它更加希望自己只用處理業務的邏輯,而不用再去關心如何把耗時的任務放到非同步執行緒中。因此係統幫我們實現了一個Loader
的實現類AsyncTaskLoader<D>
,裡面封裝了一個AsyncTask
用來執行耗時操作。但是它也是一個抽象類,我們並不能直接使用它,而是讓子類取實現它的loadInBackground
方法去處理自己的業務邏輯。
public abstract class AsyncTaskLoader<D> extends Loader<D> {
static final String TAG = "AsyncTaskLoader";
static final boolean DEBUG = false;
final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
private final CountDownLatch mDone = new CountDownLatch(1);
//表示該Task已經被post到了Handler當中,用來做延時操作。
boolean waiting;
/* Runs on a worker thread */
@Override
protected D doInBackground(Void... params) {
try {
//在後臺獲取資料。
D data = AsyncTaskLoader.this.onLoadInBackground();
return data;
//onLoadInBackground表明要取消。
} catch (OperationCanceledException ex) {
if (!isCancelled()) {
//LoaderManager仍然期望得到結果,因此繼續丟擲這個異常。
throw ex;
}
return null;
}
}
//完成。
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}
//取消。
@Override
protected void onCancelled(D data) {
if (DEBUG) Log.v(TAG, this + " onCancelled");
try {
AsyncTaskLoader.this.dispatchOnCancelled(this, data);
} finally {
mDone.countDown();
}
}
//由於實現了Runnable介面,在這裡執行executePendingTask。
@Override
public void run() {
waiting = false;
AsyncTaskLoader.this.executePendingTask();
}
//在計數達到0時,一直等待,也就是onPostExecute或onCancelled回撥了。
public void waitForLoader() {
try {
mDone.await();
} catch (InterruptedException e) {
// Ignore
}
}
}
private final Executor mExecutor;
volatile LoadTask mTask;
volatile LoadTask mCancellingTask;
long mUpdateThrottle;
long mLastLoadCompleteTime = -10000;
Handler mHandler;
public AsyncTaskLoader(Context context) {
this(context, ModernAsyncTask.THREAD_POOL_EXECUTOR);
}
private AsyncTaskLoader(Context context, Executor executor) {
super(context);
mExecutor = executor;
}
//從上一次loadInBackground返回,到下次Task執行的時間。
public void setUpdateThrottle(long delayMS) {
mUpdateThrottle = delayMS;
if (delayMS != 0) {
mHandler = new Handler();
}
}
@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad(); //這裡會回撥onCancelLoad。
mTask = new LoadTask(); //新建一個Task。
executePendingTask(); //執行這個Task。
}
//cancelLoad後回撥。
@Override
protected boolean onCancelLoad() {
//當前沒有Task在執行。
if (mTask != null) {
//如果正在等待一個被取消的任務執行完畢,那麼先取消最後的那個任務。
if (mCancellingTask != null) {
if (mTask.waiting) {
mTask.waiting = false;
mHandler.removeCallbacks(mTask);
}
mTask = null;
return false;
} else if (mTask.waiting) { //如果當前的任務正在等待被執行,那麼直接取消它。
mTask.waiting = false;
mHandler.removeCallbacks(mTask);
mTask = null;
return false;
} else {
//如果當前的任務已經開始執行了,那麼先記錄下,賦值給mCancellingTask。
boolean cancelled = mTask.cancel(false);
if (cancelled) {
mCancellingTask = mTask;
cancelLoadInBackground();
}
mTask = null;
return cancelled;
}
}
return false;
}
//dispatchOnCancelled 或者在 dispatchOnLoadComplete 是判斷是 abandon 了。
public void onCanceled(D data) {}
void executePendingTask() {
if (mCancellingTask == null && mTask != null) {
//如果mTask正在等待被執行。
if (mTask.waiting) {
mTask.waiting = false; //那麼把它從佇列中移除。
mHandler.removeCallbacks(mTask);
}
if (mUpdateThrottle > 0) {
long now = SystemClock.uptimeMillis();
if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
//放入等待佇列當中。
mTask.waiting = true;
mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
return;
}
}
//執行這個任務。
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}
void dispatchOnCancelled(LoadTask task, D data) {
onCanceled(data); //回撥onCanceled.
if (mCancellingTask == task) { //如果被取消的task執行完了。
rollbackContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mCancellingTask = null;
deliverCancellation(); //通過被取消的task執行完了。
executePendingTask(); //執行當前的Task。
}
}
void dispatchOnLoadComplete(LoadTask task, D data) {
if (mTask != task) { //如果執行完的task不是最新的。
dispatchOnCancelled(task, data);
} else {
if (isAbandoned()) { //如果被abandon了。
onCanceled(data);
} else {
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask = null;
deliverResult(data);
}
}
}
public abstract D loadInBackground();
//mTask在後臺執行時回撥這個方法。
protected D onLoadInBackground() {
return loadInBackground();
}
//mTask在執行過程中被通過cancelLoad()取消了。
public void cancelLoadInBackground() {}
public boolean isLoadInBackgroundCanceled() {
return mCancellingTask != null;
}
public void waitForLoader() {
LoadTask task = mTask;
if (task != null) {
task.waitForLoader();
}
}
}
複製程式碼
四、自定義Loader
關於如何實現AsyncTaskLoader
,系統提供了一個很好的例子,那就是CursorLoader
,它用來讀取ContentProvider
中的資料,並支援傳入關於查詢的projection/selection/selectionArgs
等條件,最終返回一個cursor
,在返回之後負責cursor
的關閉。
為了更加靈活,我們參照它的寫法,抽象出其中的關鍵程式碼,讓使用者最多隻需要負責兩樣東西:
- 資料的載入
- 資源的釋放
BaseLoader
的實現者只需要關注下面這三個方法:
//非同步載入資料,這個Bundle就是onCreateLoader時傳入的Bundle,我們可以把查詢的關鍵詞放在其中,這個是子類必須實現的方法。
protected abstract Result loadData(Bundle bundle);
//下面兩個方法是用於結果資源的回收,例如cursor的關閉,當我們的返回值只是一些基本資料型別時,並不需要實現它。
protected void releaseResult(Result result) {}
protected boolean isResultReleased(Result result) { return true; }
複製程式碼
下面是全部的實現程式碼:
public abstract class BaseDataLoader<Result> extends AsyncTaskLoader<Result> {
Result mResult;
Bundle mBundles;
CancellationSignal mCancellationSignal;
//所有的子類最多隻需要實現下面的三個方法,而不要回收資源的只用實現loadData就可以了。
protected abstract Result loadData(Bundle bundle);
protected void releaseResult(Result result) {}
protected boolean isResultReleased(Result result) { return true; }
public BaseDataLoader(Context context, Bundle bundle) {
super(context);
mBundles = bundle;
}
@Override
public Result loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
return loadData(mBundles);
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
@Override
public void cancelLoadInBackground() {
super.cancelLoadInBackground();
synchronized (this) {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
}
}
}
@Override
public void deliverResult(Result result) {
if (isReset()) {
if (result != null && !isResultReleased(result)) {
releaseResult(result);
}
return;
}
Result oldResult = mResult;
mResult = result;
if (isStarted()) {
super.deliverResult(result);
}
if (oldResult != null && oldResult != result && !isResultReleased(oldResult)) {
releaseResult(oldResult);
}
}
@Override
protected void onStartLoading() {
if (mResult != null) {
deliverResult(mResult);
}
if (takeContentChanged() || mResult == null) {
forceLoad();
}
}
@Override
protected void onStopLoading() {
cancelLoad();
}
@Override
public void onCanceled(Result result) {
if (result != null && !isResultReleased(result)) {
releaseResult(result);
}
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
if (mResult != null && !isResultReleased(mResult)) {
releaseResult(mResult);
}
mResult = null;
}
}
複製程式碼