安卓權威編程指南-筆記(第24章 Looper Handler 和 HandlerThread)

來源:互聯網
上載者:User

標籤:重複   resources   覆蓋   標識   關係   turn   int   android   圖片   

AsyncTask是執行後台線程的最簡單方式,但它不適用於那些重複且長時間啟動並執行任務。

 

1. Looper

Android中,線程擁有一個訊息佇列(message queue),使用訊息佇列的線程叫做訊息迴圈(message loop)。訊息迴圈會迴圈檢查隊列上是否有新訊息。

訊息迴圈由線程和looper組成,Looper對象管理著線程的訊息佇列。

主線程就是個訊息迴圈,因此也擁有Looper,主線程的所有工作都是由其looper完成的,looper不斷的從訊息佇列中抓去訊息,然後完成訊息指定的任務。

 

2. Message

訊息是Message類的一個執行個體,它有好幾個執行個體變數,其中有三個需要在實現時定義。

1.What:使用者定義的int型訊息代碼,用來描述訊息。

2.obj:隨訊息發送的使用者指定對象。

3.target: 處理訊息的Handler。

 

3. Handler

Message的目標(target)是Handler類的一個執行個體,Handler可看作message handler的簡稱,建立Message時,它會自動與一個handler相關聯,message待處理時,Handler對象負責觸發訊息處理事件、

Handler不僅僅是處理Message的目標,也是建立和發布Message的介面。

 

4. 關係

Looper擁有Message收件匣,所以Message必須在Looper上發布或處理。為與Looper協同工作,Handler總是引用著它。

一個Handler僅與一個Looper相關聯,一個Message也僅於一個目標Handler(也稱作Message目標)相關聯。Looper擁有整個Message隊列,多個Message可以引用同一目標Handler。

多個Handler也可與一個Looper相關聯,這意味著一個Handler的Message可能與另一個Handler的Message存放在同一訊息佇列中。

 

5. 使用Handler 

通常不需要手動設定訊息的目標Handler。建立資訊時,調用Handler.obtainMessage()方法。當傳入其他訊息欄位給他時,該方法會自動化佈建目標給Handler對象。

為避免建立新的Message對象,Handler.obtainMessage()方法會從公用迴圈吃擷取訊息。  

一旦取得Message,就可以調用sendToTarget()方法將其發送給它的Handler,然後Handler會將這個Message放置在Looper訊息佇列的尾部。

Looper取得訊息佇列中的特定訊息後,會將它發送給訊息目標去處理。訊息一般是在目標的Handler.handleMessage()實現方法中進行處理。

 

6. HandlerThread

HandlerThread類幫我們完成了建立looper的過程,只要繼承它就能省去一些工作。

public class ThumbnailDownloader<T> extends HandlerThread {    private static final String TAG = "ThumbnailDownloader";    private static final int MESSAGE_DOWNLOAD = 0; //標識下載請求    private Boolean mHasQuit = false;    private Handler mRequestHandler; //儲存對Handler的引用,這個Handler負責在ThumbnailDownloader後台線程上管理下載請求訊息佇列。這個Handler也負責從訊息佇列裡取出並處理下載請求訊息。    //onLooperPrepared()在Looper首次檢查訊息佇列之前調用的。    @Override    protected void onLooperPrepared(){        mRequestHandler = new Handler(){            @Override            public void handleMessage(Message msg){  //隊列中的下載訊息取出並可以處理時,就會觸發調用Handler.handleMessage()方法。                //處理操作            }        };    }    public void queueThumbnail(T target, String url) { //當傳入其他訊息欄位給它時,該方法會自動化佈建目標給Handler對象(obtainMessage)            //sendToTarget()方法將Message發送給它的Handler,然後Handler會將這個Message放置在Looper訊息佇列的尾部。               mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD,target).sendToTarget();    }    public void clearQueue(){        mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);    }    }

 

主線程中這樣調用:

mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);mThumbnailDownloader.start();mThumbnailDownloader.getLooper(); //在start()方法之後調用getLooper()方法是一種保證線程就緒的處理方式。可以避免潛在競爭。// 在需要的時候調用mThumbnailDownloader.queueThumbnail(holder, url);

 

7.線程互動

主線程現在能夠適時調用這個線程的方法,用於下載圖片了。但是還存在一個問題,那就是下載線程下載完一個任務以後如何更新視圖呢?我們知道 UI 只能在主線程裡更新,所以我們採用在主線程裡聲明一個 Handler,傳遞給下載線程,讓下載線程在下載完成後在主線程執行更新操作。因為不能直接引用主線程的方法,故而在這裡用到了回調。

7.1下載線程中:

// ThumbnailDownloader,也就是下載線程中// 成員聲明private Handler mResponseHandler;private ThumbnailDowloadListener<T> mThumbnailDownloadListener;// 回調介面public interface ThumbnailDowloadListener<T> {/*         * 圖片下載完成,可以交給UI去顯示時,介面中的方法就會被調用。         * 會使用這個方法把處理已下載圖片的任務代理給另一個類(PhotoGalleryFragment),這樣ThumbnailDownloader就可以把下載結果傳給其他視圖對象。         */    void onThumbnailDownloaded(T target, Bitmap thumbnail);}public void setThumbnailDownloaderListener(ThumbnailDowloadListener<T> listener) {    mThumbnailDownloadListener = listener;}// 通過建構函式傳遞主線程的 Handlerpublic ThumbnailDowloader(Handler responseHandler) {    super(TAG);    mResponseHandler = responseHandler;}

這樣,主線程通過調用這些方法,就能夠讓下載線程擷取到主線程的 Handler 和回調介面執行個體。

7.2主線程中

// 成員聲明private ThumbnailDowloader<PhotoHolder> mThumbnailDownloader;// 傳遞執行個體給下載線程// 這個 Handler 在主線程中建立,所以是和主線程 Looper 相關聯的Handler responseHandler = new Handler(); mThumbnailDownloader = new ThumbnailDowloader<>(responseHandler);mThumbnailDownloader.setThumbnailDownloaderListener(    new ThumbnailDowloader.ThumbnailDowloadListener<PhotoHolder>() {        @Override       public void onThumbnailDownloaded(PhotoHolder target, Bitmap thumbnail) {            Drawable drawable = new BitmapDrawable(getResources(), thumbnail);            target.bindDrawable(drawable);        }    });

8.線程互動

現在,通過 mResponseHandler,下載線程能夠訪問與主線程 Looper 綁定的 Handler。同時,還有 ThumbnailDownloadListener 使用返回的 Bitmap 執行 UI 更新操作。具體來說, 就是通過 onThumbnailDownloaded 實現,使用新下載的 Bitmap 來設定 PhotoHolder 的 Drawable。 
和在下載線程上把下載圖片的請求放入訊息佇列類似,我們也可以返回定製 Message 給主線程,要求顯示已下載圖片。不過,這需要另一個 Handler 子類,以及一個 handleMessage(…) 覆蓋方法。方便起見,我們轉而使用另一個方便的 Handler 方法——post(Runnable)。

mResponseHandler.post(new Runnable() {    @Override    public void run() {        if (mRequestMap.get(target) != url ||                mHasQuit) {            return;        }        mRequestMap.remove(target);        mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);    }});

在這裡,建立的 Runnable 對象會被當成 Message 的回調方法,直接執行 run() 方法,所以相當於發送一個訊息,裡面寫明了怎麼做,而不是把對象和訊息類型發給 Handler,讓 Handler 決定怎麼做。

 

安卓權威編程指南-筆記(第24章 Looper Handler 和 HandlerThread)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.