AsyncTaskLoader設計原理大揭祕

簡介

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完成數據加載後會回調LoaderInfoonLoadComplete()方法。

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

外部就是通過調用LoaderregisterListener()方法將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開發來說是這樣),那就從MainActivityonCreate()來看起吧。

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

繼續LoaderManagerdoStart()方法:

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

進入LoaderstartLoading()方法看看:

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.文章寫的倉促,如果有有問題的地方歡迎指出。

發佈了26 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章