Android 與UI線程通信

2018-08-02 18:27 更新

編寫:AllenZheng1991 - 原文:http://developer.android.com/training/multiple-threads/communicate-ui.html

在前面的課程中你學(xué)習(xí)了如何在一個被ThreadPoolExecutor管理的線程中開啟一個任務(wù)。最后這一節(jié)課將會向你展示如何從執(zhí)行的任務(wù)中發(fā)送數(shù)據(jù)給運行在UI線程中的對象。這個功能允許你的任務(wù)可以做后臺工作,然后把得到的結(jié)果數(shù)據(jù)轉(zhuǎn)移給UI元素使用,例如位圖數(shù)據(jù)。

任何一個APP都有自己特定的一個線程用來運行UI對象,比如View對象,這個線程我們稱之為UI線程。只有運行在UI線程中的對象能訪問運行在其它線程中的對象。因為你的任務(wù)執(zhí)行的線程來自一個線程池而不是執(zhí)行在UI線程,所以他們不能訪問UI對象。為了把數(shù)據(jù)從一個后臺線程轉(zhuǎn)移到UI線程,需要使用一個運行在UI線程里的Handler。

在UI線程中定義一個Handler

Handler屬于Android系統(tǒng)的線程管理框架的一部分。一個Handler對象用于接收消息和執(zhí)行處理消息的代碼。一般情況下,如果你為一個新線程創(chuàng)建了一個Handler,你還需要創(chuàng)建一個Handler,讓它與一個已經(jīng)存在的線程關(guān)聯(lián),用于這兩個線程之間的通信。如果你把一個Handler關(guān)聯(lián)到UI線程,處理消息的代碼就會在UI線程中執(zhí)行。

你可以在一個用于創(chuàng)建你的線程池的類的構(gòu)造方法中實例化一個Handler對象,并把它定義為全局變量,然后通過使用Handler (Looper) 這一構(gòu)造方法實例化它,用于關(guān)聯(lián)到UI線程。Handler(Looper)這一構(gòu)造方法需要傳入了一個Looper對象,它是Android系統(tǒng)的線程管理框架中的另一部分。當你在一個特定的Looper實例的基礎(chǔ)上去實例化一個Handler時,這個HandlerLooper運行在同一個線程里。例如:

private PhotoManager() {
...
    // Defines a Handler object that's attached to the UI thread
    mHandler = new Handler(Looper.getMainLooper()) {
    ...

在這個Handler里需要重寫handleMessage()方法。當這個Handler接收到由另外一個線程管理的Handler發(fā)送過來的新消息時,Android系統(tǒng)會自動調(diào)用這個方法,而所有線程對應(yīng)的Handler都會收到相同信息。例如:

        /*
         * handleMessage() defines the operations to perform when
         * the Handler receives a new Message to process.
         */
        @Override
        public void handleMessage(Message inputMessage) {
            // Gets the image task from the incoming Message object.
            PhotoTask photoTask = (PhotoTask) inputMessage.obj;
            ...
        }
    ...
    }
}

下一部分將向你展示如何用Handler轉(zhuǎn)移數(shù)據(jù)。

把數(shù)據(jù)從一個任務(wù)中轉(zhuǎn)移到UI線程

為了從一個運行在后臺線程的任務(wù)對象中轉(zhuǎn)移數(shù)據(jù)到UI線程中的一個對象,首先需要存儲任務(wù)對象中的數(shù)據(jù)和UI對象的引用;接下來傳遞任務(wù)對象和狀態(tài)碼給實例化Handler的那個對象。在這個對象里,發(fā)送一個包含任務(wù)對象和狀態(tài)的MessageHandler也運行在UI線程中,所以它可以把數(shù)據(jù)轉(zhuǎn)移到UI線程。

在任務(wù)對象中存儲數(shù)據(jù)

比如這里有一個Runnable,它運行在一個編碼了一個Bitmap且存儲這個Bitmap到父類PhotoTask對象里的后臺線程。這個Runnable同樣也存儲了狀態(tài)碼DECODE_STATE_COMPLETED。

// A class that decodes photo files into Bitmaps
class PhotoDecodeRunnable implements Runnable {
    ...
    PhotoDecodeRunnable(PhotoTask downloadTask) {
        mPhotoTask = downloadTask;
    }
    ...
    // Gets the downloaded byte array
    byte[] imageBuffer = mPhotoTask.getByteBuffer();
    ...
    // Runs the code for this task
    public void run() {
        ...
        // Tries to decode the image buffer
        returnBitmap = BitmapFactory.decodeByteArray(
                imageBuffer,
                0,
                imageBuffer.length,
                bitmapOptions
        );
        ...
        // Sets the ImageView Bitmap
        mPhotoTask.setImage(returnBitmap);
        // Reports a status of "completed"
        mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
        ...
    }
    ...
}
...

PhotoTask類還包含一個用于顯示BitmapImageView的引用。雖然BitmapImageViewImageView的引用在同一個對象中,但你不能把這個Bitmap分配給ImageView去顯示,因為它們并沒有運行在UI線程中。

這時,下一步應(yīng)該發(fā)送這個狀態(tài)給PhotoTask對象。

發(fā)送狀態(tài)取決于對象層次

PhotoTask是下一個層次更高的對象,它包含將要展示數(shù)據(jù)的編碼數(shù)據(jù)和View對象的引用。它會收到一個來自PhotoDecodeRunnable的狀態(tài)碼,并把這個狀態(tài)碼單獨傳遞到一個包含線程池和Handler實例的對象:

public class PhotoTask {
    ...
    // Gets a handle to the object that creates the thread pools
    sPhotoManager = PhotoManager.getInstance();
    ...
    public void handleDecodeState(int state) {
        int outState;
        // Converts the decode state to the overall state.
        switch(state) {
            case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
                outState = PhotoManager.TASK_COMPLETE;
                break;
            ...
        }
        ...
        // Calls the generalized state method
        handleState(outState);
    }
    ...
    // Passes the state to PhotoManager
    void handleState(int state) {
        /*
         * Passes a handle to this task and the
         * current state to the class that created
         * the thread pools
         */
        sPhotoManager.handleState(this, state);
    }
    ...
}

轉(zhuǎn)移數(shù)據(jù)到UI

PhotoTask對象那里,PhotoManager對象收到了一個狀態(tài)碼和一個PhotoTask對象的引用。因為狀態(tài)碼是TASK_COMPLETE,所以創(chuàng)建一個Message應(yīng)該包含狀態(tài)和任務(wù)對象,然后把它發(fā)送給Handler

public class PhotoManager {
    ...
    // Handle status messages from tasks
    public void handleState(PhotoTask photoTask, int state) {
        switch (state) {
            ...
            // The task finished downloading and decoding the image
            case TASK_COMPLETE:
                /*
                 * Creates a message for the Handler
                 * with the state and the task object
                 */
                Message completeMessage =
                        mHandler.obtainMessage(state, photoTask);
                completeMessage.sendToTarget();
                break;
            ...
        }
        ...
    }

最終,Handler.handleMessage()會檢查每個傳入進來的Message,如果狀態(tài)碼是TASK_COMPLETE,這時任務(wù)就完成了,而傳入的Message里的PhotoTask對象里同時包含一個Bitmap和一個ImageView。因為Handler.handleMessage()運行在UI線程里,所以它能安全地轉(zhuǎn)移Bitmap數(shù)據(jù)給ImageView

private PhotoManager() {
        ...
            mHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message inputMessage) {
                    // Gets the task from the incoming Message object.
                    PhotoTask photoTask = (PhotoTask) inputMessage.obj;
                    // Gets the ImageView for this task
                    PhotoView localView = photoTask.getPhotoView();
                    ...
                    switch (inputMessage.what) {
                        ...
                        // The decoding is done
                        case TASK_COMPLETE:
                            /*
                             * Moves the Bitmap from the task
                             * to the View
                             */
                            localView.setImageBitmap(photoTask.getImage());
                            break;
                        ...
                        default:
                            /*
                             * Pass along other messages from the UI
                             */
                            super.handleMessage(inputMessage);
                    }
                    ...
                }
                ...
            }
            ...
    }
...
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號