W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
編寫:AllenZheng1991 - 原文:http://developer.android.com/training/multiple-threads/communicate-ui.html
在前面的課程中你學(xué)習(xí)了如何在一個(gè)被ThreadPoolExecutor管理的線程中開啟一個(gè)任務(wù)。最后這一節(jié)課將會(huì)向你展示如何從執(zhí)行的任務(wù)中發(fā)送數(shù)據(jù)給運(yùn)行在UI線程中的對象。這個(gè)功能允許你的任務(wù)可以做后臺(tái)工作,然后把得到的結(jié)果數(shù)據(jù)轉(zhuǎn)移給UI元素使用,例如位圖數(shù)據(jù)。
任何一個(gè)APP都有自己特定的一個(gè)線程用來運(yùn)行UI對象,比如View對象,這個(gè)線程我們稱之為UI線程。只有運(yùn)行在UI線程中的對象能訪問運(yùn)行在其它線程中的對象。因?yàn)槟愕娜蝿?wù)執(zhí)行的線程來自一個(gè)線程池而不是執(zhí)行在UI線程,所以他們不能訪問UI對象。為了把數(shù)據(jù)從一個(gè)后臺(tái)線程轉(zhuǎn)移到UI線程,需要使用一個(gè)運(yùn)行在UI線程里的Handler。
Handler屬于Android系統(tǒng)的線程管理框架的一部分。一個(gè)Handler對象用于接收消息和執(zhí)行處理消息的代碼。一般情況下,如果你為一個(gè)新線程創(chuàng)建了一個(gè)Handler,你還需要?jiǎng)?chuàng)建一個(gè)Handler,讓它與一個(gè)已經(jīng)存在的線程關(guān)聯(lián),用于這兩個(gè)線程之間的通信。如果你把一個(gè)Handler關(guān)聯(lián)到UI線程,處理消息的代碼就會(huì)在UI線程中執(zhí)行。
你可以在一個(gè)用于創(chuàng)建你的線程池的類的構(gòu)造方法中實(shí)例化一個(gè)Handler對象,并把它定義為全局變量,然后通過使用Handler (Looper) 這一構(gòu)造方法實(shí)例化它,用于關(guān)聯(lián)到UI線程。Handler(Looper)這一構(gòu)造方法需要傳入了一個(gè)Looper對象,它是Android系統(tǒng)的線程管理框架中的另一部分。當(dāng)你在一個(gè)特定的Looper實(shí)例的基礎(chǔ)上去實(shí)例化一個(gè)Handler時(shí),這個(gè)Handler與Looper運(yùn)行在同一個(gè)線程里。例如:
private PhotoManager() {
...
// Defines a Handler object that's attached to the UI thread
mHandler = new Handler(Looper.getMainLooper()) {
...
在這個(gè)Handler里需要重寫handleMessage()方法。當(dāng)這個(gè)Handler接收到由另外一個(gè)線程管理的Handler發(fā)送過來的新消息時(shí),Android系統(tǒng)會(huì)自動(dòng)調(diào)用這個(gè)方法,而所有線程對應(yīng)的Handler都會(huì)收到相同信息。例如:
/*
* 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ù)。
為了從一個(gè)運(yùn)行在后臺(tái)線程的任務(wù)對象中轉(zhuǎn)移數(shù)據(jù)到UI線程中的一個(gè)對象,首先需要存儲(chǔ)任務(wù)對象中的數(shù)據(jù)和UI對象的引用;接下來傳遞任務(wù)對象和狀態(tài)碼給實(shí)例化Handler的那個(gè)對象。在這個(gè)對象里,發(fā)送一個(gè)包含任務(wù)對象和狀態(tài)的Message給Handler也運(yùn)行在UI線程中,所以它可以把數(shù)據(jù)轉(zhuǎn)移到UI線程。
比如這里有一個(gè)Runnable,它運(yùn)行在一個(gè)編碼了一個(gè)Bitmap且存儲(chǔ)這個(gè)Bitmap到父類PhotoTask對象里的后臺(tái)線程。這個(gè)Runnable同樣也存儲(chǔ)了狀態(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類還包含一個(gè)用于顯示Bitmap的ImageView的引用。雖然Bitmap和ImageViewImageView的引用在同一個(gè)對象中,但你不能把這個(gè)Bitmap分配給ImageView去顯示,因?yàn)樗鼈儾]有運(yùn)行在UI線程中。
這時(shí),下一步應(yīng)該發(fā)送這個(gè)狀態(tài)給PhotoTask
對象。
PhotoTask是下一個(gè)層次更高的對象,它包含將要展示數(shù)據(jù)的編碼數(shù)據(jù)和View對象的引用。它會(huì)收到一個(gè)來自PhotoDecodeRunnable的狀態(tài)碼,并把這個(gè)狀態(tài)碼單獨(dú)傳遞到一個(gè)包含線程池和Handler實(shí)例的對象:
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);
}
...
}
從PhotoTask對象那里,PhotoManager對象收到了一個(gè)狀態(tài)碼和一個(gè)PhotoTask對象的引用。因?yàn)闋顟B(tài)碼是TASK_COMPLETE,所以創(chuàng)建一個(gè)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()會(huì)檢查每個(gè)傳入進(jìn)來的Message,如果狀態(tài)碼是TASK_COMPLETE,這時(shí)任務(wù)就完成了,而傳入的Message里的PhotoTask對象里同時(shí)包含一個(gè)Bitmap和一個(gè)ImageView。因?yàn)?a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" target="_blank">Handler.handleMessage()運(yùn)行在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);
}
...
}
...
}
...
}
...
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: