Loader 知識梳理(3) - 自定義Loader

一、概述

前面我們比較關注的是LoaderManager的原理和使用,如果我們只是需要通過LoaderManager來讀取ContentProvider封裝的數(shù)據(jù),那么采用系統(tǒng)已經(jīng)定義好的CursorLoader就可以了,但是有的時候,我們的數(shù)據(jù)可能不是用ContentProvider封裝的,那么這時候就要學會如何定義自己的Loader。

二、Loader<D>

對于LoaderManager來說,它所看到的只是一個Loader類,并調用它對應的方法,這些方法分為三類:

  • 狀態(tài)標志: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執(zhí)行完,且沒有調用stopLoading或reset。
    public boolean isStarted() {
        return mStarted;
    }

    //在該狀態(tài)下,不應該上報新的數(shù)據(jù),并且應該保持著最后一次上報的數(shù)據(jù)直到被reset。
    public boolean isAbandoned() {
        return mAbandoned;
    }

    //沒有啟動,或者調用了reset方法。
    public boolean isReset() {
        return mReset;
    }
    
    <!-- 開始任務相關 -->
    //開始任務。    
    public final void startLoading() {
        mStarted = true;
        mReset = false;
        mAbandoned = false;
        onStartLoading();
    }
    
    //開始任務后回調該方法,調用者重寫該方法來加載數(shù)據(jù)。
    protected void onStartLoading() {}

    <!-- 取消任務相關 -->   
   //取消任務。
   //1.返回 false 表示不能取消,有可能是已經(jīng)完成,或者 startLoading 還沒有被調用。
   //2.這不是一個立刻的過程,因為加載是在后臺線程中運行的。
   public boolean cancelLoad() {
        return onCancelLoad();
    }
    
    //調用者重寫該方法來做取消后的操作。
    protected boolean onCancelLoad() {
        return false;
    }
 
    <!-- 強制刷新數(shù)據(jù)相關 -->   
    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狀態(tài),那么收到變化的通知就立即重新加載,否則記錄下這個標志mContentChanged。
    public void onContentChanged() {
        if (mStarted) {
            forceLoad();
        } else {
            mContentChanged = true;
        }
    }

}

可以看到,在Loader<D>中,提供的是給LoaderManager調用的接口,但是它并沒有真正地執(zhí)行操作,而是在startLoading等方法之后,提供了onStartLoading等回調,讓子類在這些回調中去執(zhí)行對應的操作。

三、AsyncTaskLoader<D>

對于Loader的使用者來說,它更加希望自己只用處理業(yè)務的邏輯,而不用再去關心如何把耗時的任務放到異步線程中。因此系統(tǒng)幫我們實現(xiàn)了一個Loader的實現(xiàn)類AsyncTaskLoader<D>,里面封裝了一個AsyncTask用來執(zhí)行耗時操作。但是它也是一個抽象類,我們并不能直接使用它,而是讓子類取實現(xiàn)它的loadInBackground方法去處理自己的業(yè)務邏輯。

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已經(jīng)被post到了Handler當中,用來做延時操作。
        boolean waiting;

        /* Runs on a worker thread */
        @Override
        protected D doInBackground(Void... params) {
            try {
                //在后臺獲取數(shù)據(jù)。
                D data = AsyncTaskLoader.this.onLoadInBackground();
                return data;
            //onLoadInBackground表明要取消。
            } catch (OperationCanceledException ex) { 
                if (!isCancelled()) {
                    //LoaderManager仍然期望得到結果,因此繼續(xù)拋出這個異常。
                    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();
            }
        }

        //由于實現(xiàn)了Runnable接口,在這里執(zhí)行executePendingTask。
        @Override
        public void run() {
            waiting = false;
            AsyncTaskLoader.this.executePendingTask();
        }

        //在計數(shù)達到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執(zhí)行的時間。
    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(); //執(zhí)行這個Task。
    }

    //cancelLoad后回調。
    @Override
    protected boolean onCancelLoad() {
        //當前沒有Task在執(zhí)行。
        if (mTask != null) {
            //如果正在等待一個被取消的任務執(zhí)行完畢,那么先取消最后的那個任務。
            if (mCancellingTask != null) {
                if (mTask.waiting) {
                    mTask.waiting = false;
                    mHandler.removeCallbacks(mTask);
                }
                mTask = null;
                return false;
            } else if (mTask.waiting) { //如果當前的任務正在等待被執(zhí)行,那么直接取消它。
                mTask.waiting = false;
                mHandler.removeCallbacks(mTask);
                mTask = null;
                return false;
            } else {
                //如果當前的任務已經(jīng)開始執(zhí)行了,那么先記錄下,賦值給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正在等待被執(zhí)行。
            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;
                }
            }
            //執(zhí)行這個任務。
            mTask.executeOnExecutor(mExecutor, (Void[]) null);
        }
    }

    void dispatchOnCancelled(LoadTask task, D data) {
        onCanceled(data); //回調onCanceled.
        if (mCancellingTask == task) { //如果被取消的task執(zhí)行完了。
            rollbackContentChanged(); 
            mLastLoadCompleteTime = SystemClock.uptimeMillis();
            mCancellingTask = null;
            deliverCancellation(); //通過被取消的task執(zhí)行完了。
            executePendingTask(); //執(zhí)行當前的Task。
        }
    }

    void dispatchOnLoadComplete(LoadTask task, D data) {
        if (mTask != task) { //如果執(zhí)行完的task不是最新的。
            dispatchOnCancelled(task, data);
        } else {
            if (isAbandoned()) { //如果被abandon了。
                onCanceled(data);
            } else {
                commitContentChanged();
                mLastLoadCompleteTime = SystemClock.uptimeMillis();
                mTask = null;
                deliverResult(data);
            }
        }
    }

    public abstract D loadInBackground();
    
    //mTask在后臺執(zhí)行時回調這個方法。
    protected D onLoadInBackground() {
        return loadInBackground();
    }
    
    //mTask在執(zhí)行過程中被通過cancelLoad()取消了。
    public void cancelLoadInBackground() {}

    public boolean isLoadInBackgroundCanceled() {
        return mCancellingTask != null;
    }

    public void waitForLoader() {
        LoadTask task = mTask;
        if (task != null) {
            task.waitForLoader();
        }
    }

}

四、自定義Loader

關于如何實現(xiàn)AsyncTaskLoader,系統(tǒng)提供了一個很好的例子,那就是CursorLoader,它用來讀取ContentProvider中的數(shù)據(jù),并支持傳入關于查詢的projection/selection/selectionArgs等條件,最終返回一個cursor,在返回之后負責cursor的關閉。
為了更加靈活,我們參照它的寫法,抽象出其中的關鍵代碼,讓使用者最多只需要負責兩樣東西:

  • 數(shù)據(jù)的加載
  • 資源的釋放

BaseLoader的實現(xiàn)者只需要關注下面這三個方法:

    //異步加載數(shù)據(jù),這個Bundle就是onCreateLoader時傳入的Bundle,我們可以把查詢的關鍵詞放在其中,這個是子類必須實現(xiàn)的方法。
    protected abstract Result loadData(Bundle bundle);
    //下面兩個方法是用于結果資源的回收,例如cursor的關閉,當我們的返回值只是一些基本數(shù)據(jù)類型時,并不需要實現(xiàn)它。
    protected void releaseResult(Result result) {}
    protected boolean isResultReleased(Result result) { return true; }

下面是全部的實現(xiàn)代碼:

public abstract class BaseDataLoader<Result> extends AsyncTaskLoader<Result> {

    Result mResult;
    Bundle mBundles;
    CancellationSignal mCancellationSignal;

    //所有的子類最多只需要實現(xiàn)下面的三個方法,而不要回收資源的只用實現(xiàn)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;
    }

}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1 背景## 在Android中任何耗時的操作都不能放在UI主線程中,所以耗時的操作都需要使用異步實現(xiàn)。同樣的,在...
    我是昵稱閱讀 1,348評論 0 3
  • Android開發(fā)者都經(jīng)歷過APP UI開發(fā)不當 會造成overDraw,導致APP UI渲染過慢,但是很多人卻沒...
    Tamic閱讀 16,207評論 30 104
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,138評論 25 708
  • 一、概述 剛開始學習Loader的時候,只是使用CursorLoader把它當作加載封裝在ContentProvi...
    澤毛閱讀 10,283評論 4 8
  • 25: 身體原因今天全線崩盤,希望明天恢復正常 26:聽力第一題,閱讀模擬至少2完成,單詞
    WB16閱讀 200評論 0 0

友情鏈接更多精彩內容