轉(zhuǎn)載請注明出處
作者:developerHaoz
Github 地址:developerHaoz
本文的主要內(nèi)容
- Handler 是什么
- Handler 的兩個體系
- Message
一胡岔、Handler是什么
Handler 是 Android 中引入的一種讓開發(fā)者參與處理線程中消息循環(huán)的機制责静,Handler直接繼承自 Object践磅,每個 Handler 都關聯(lián)了一個線程龄糊,每個線程內(nèi)部都維護了一個消息隊列 MessageQueue,這樣 Handler 實際上也就關聯(lián)了一個消息隊列日丹。這樣就可以通過 Handler 將 Message 和 Runnable 對象發(fā)送到該Handler所關聯(lián)線程的 MessageQueue(消息隊列)中,然后該消息隊列一直在循環(huán)拿出一個 Message,對其進行處理壳炎,處理完之后拿出下一個 Message,繼續(xù)處理
Handler 可以用來在多線程之間進行通信逼侦,在另一個線程中去更新 UI 線程中的 UI 控件只是 Handler 使用中的一種典型案例匿辩,除此之外,Handler 還可以做其他很多的事情榛丢,Handler 是 Thread 的代言人铲球,是多線程之間通信的橋梁,通過 Handler晰赞,我們可以在一個線程中控制另一個線程去做某些事
二稼病、Handler的兩個體系
Handler 可以把一個 Message 對象或者 Runnable 對象壓入到消息隊列中,進而在UI線程中獲取Message 或者執(zhí)行 Runnable 對象掖鱼,Handler 壓入消息隊列有兩大體系然走,Post 和 sendMessage
- Post:Post允許把一個 Runnable 對象入隊到消息隊列中,它的方法有:post(Runnable)锨用、PostAtTime(Runnable, long)丰刊、postDelayed(Runnable, long)
- sendMessage:sendMessage允許把一個包含消息數(shù)據(jù)的Message對象壓入到消息隊列中,它的方法有sendEmptyMessage(int)增拥、sendMessage(Message)啄巧、sendMessageAtTime(Message, long)、sendMessageDelayed(Message, long)
從上面的各種方法可以看出掌栅,不管是 post 還是 sendMessage 都具有多種方法秩仆,它們可以設定Runnable 對象和 Message 對象被入隊到消息隊列中,是立即執(zhí)行還是延遲執(zhí)行
1猾封、Post
對于 Handler 的 Post 方式來說澄耍,它會傳遞一個 Runnable 對象到消息隊列中,在這個 Runnable 對象中晌缘,重寫 run() 方法齐莲,一般是在這個 run() 方法中寫入需要在UI線程中的操作
在 Handler 中,關于 Post 方式的方法有
方法名稱 | 作用 |
---|---|
boolean post(Runnable r) | 把一個 Runnabled 入隊到消息隊列中磷箕,UI 線程從消息隊列中取出這個對象后选酗,立即執(zhí)行 |
boolean postAtTime(Runnable r, long uptimeMills) | 把一個 Runnable 入隊到消息隊列中,UI線程從消息獨立列中取出這個對象后岳枷,在特定的時間執(zhí)行 |
boolean postDelayed(Runnable r, long delayMills) | 把一個 Runnable 入隊到消息隊列中芒填,UI線程從消息隊列中能夠取出這個對象后呜叫,延遲delayMills秒執(zhí)行 |
void removeCallbacks(Runnable r) | 從消息隊列中移除一個 Runnable 對象 |
示例代碼
public void onClick() {
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
// 將TextView的內(nèi)容進行修改
mTvShowInfo.setText("This is a test");
}
});
}
}).start();
}
有一點需要注意的是,對于 Post 方式而言殿衰,它其中的 Runnable 對象的 run() 方法的代碼朱庆,均執(zhí)行在UI線程上
,所以如果是不能在 UI 線程上執(zhí)行的操作闷祥,如網(wǎng)絡請求之類的娱颊,一樣無法使用Post方式執(zhí)行
2、sendMessage
在Handler中蜀踏,與Message發(fā)送消息相關的方法
方法 | 作用 |
---|---|
Message obtainMessage() | 獲取一個Message對象 |
boolean sendMessage() | 發(fā)送一個Message對象到消息隊列中维蒙,并在UI線程取到消息之后掰吕,立即執(zhí)行 |
boolean sendMessageDelayed() | 發(fā)送一個Message對象到消息隊列中果覆,在 UI 線程取到消息后,延遲執(zhí)行 |
boolean sendEmptyMessage(int what) | 發(fā)送一個空Message對象到消息隊列中殖熟,并在 UI 線程取到消息之后局待,立即執(zhí)行 |
boolean sendEmptyMessageDelayed(int what) | 發(fā)送一個空Message對象到消息隊列中,并在 UI 線程取到消息之后菱属,延遲執(zhí)行 |
void removeMessage() | 從消息隊列中移除一個未響應的消息 |
示例代碼
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
String testStr = "This is a test";
Message message = Message.obtain();
message.obj = testStr;
mHandler.sendMessage(message);
}
});
}
}).start();
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == RESULT_OK_HANDLER) {
String infoStr = (String)msg.obj;
mHandlerTvShowInfo.setText(infoStr);
}
}
};
三钳榨、Message
Handler 如果使用 sendMessage 的方式把消息入隊到消息隊列中,需要傳遞一個 Message 對象纽门,而在 Handler薛耻,需要重寫 handleMessage() 方法,用于獲取工作線程中傳遞過來的消息赏陵,此方法運行在 UI 線程上
1饼齿、獲取一個 Message 對象
一般并不推薦直接使用它的構(gòu)造方法得到,而是建議通過 Message.obtain()
這個靜態(tài)方法或者 Handler.obtainMessage()
獲取蝙搔。Message.obtain() 會從消息池中獲取一個 Message 對象缕溉,如果消息池是空的,才會使用構(gòu)造方法實例化一個新的 Message吃型,這樣有利用消息資源的重復利用证鸥,消息的上限為10個,Handler.obtainMessage() 具有多個重載方法勤晚,查看源碼可以知道枉层,Handler.obtainMessage() 在內(nèi)部其實也是調(diào)用 Message.obtain()
public final Message obtainMessage()
{
return Message.obtain(this);
}
2、設置赐写、獲取和傳遞數(shù)據(jù)
Message是一個 final 類鸟蜡,所以不可被繼承,Message 封裝了線程中傳遞過來的消息血淌,如果對于一般的數(shù)據(jù)矩欠,Message 提供了 getData() 和 setData 方法來獲取和設置數(shù)據(jù)财剖,其中操作的數(shù)據(jù)是一個Bundle 對象,這個 Bundle 對象提供一系列的 getXxx() 和 setXxx() 方法用于傳遞基本數(shù)據(jù)類型的鍵值對,使用起來比較簡單癌淮。
示例代碼
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
String testStr = "This is a test";
Message message = Message.obtain();
Bundle testBundle = new Bundle();
testBundle.putString(KEY_STRING, testStr);
message.setData(testBundle);
message.what = RESULT_OK_HANDLER;
mHandler.sendMessage(message);
}
});
}
}).start();
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == RESULT_OK_HANDLER) {
String infoStr = msg.getData().getString(KEY_STRING);
mHandlerTvShowInfo.setText(infoStr);
}
}
};
而對于復雜的數(shù)據(jù)類型躺坟,如一個對象的傳遞就要相對復雜一些,在 Bundle 中提供了兩個方法乳蓄,專門用來傳遞對象的咪橙,但是這兩個方法也有相應的限制,需要實現(xiàn)特定的接口虚倒,當然美侦,一些 Android 自帶的類,其實已經(jīng)實現(xiàn)了這兩個接口中的某一個魂奥,可以直接使用
- putParcelable(String key, Parcelable value):需要傳遞的對象類實現(xiàn)Parcelable接口
- putSerializable(String key, Serializable value):需要傳遞的對象類實現(xiàn)Serializable接口
還有另外一種方式在 Message 中傳遞對象咖为,那就是使用 Message 自帶的 obj 屬性,它是一個
Object 類型歌懒,所以可以傳遞任意類型的對象茶袒,Message 自帶的還有如下幾個屬性
屬性 | 作用 |
---|---|
int arg1 | 參數(shù)一,用于傳遞不復雜的數(shù)據(jù)哈蝇,復雜數(shù)據(jù)用 setData() 傳遞 |
int arg2 | 參數(shù)二棺妓,用于傳遞不復雜的數(shù)據(jù),復雜數(shù)據(jù)用 setData() 傳遞 |
Object obj | 傳遞一個任意的對象 |
Messaenger replyTo | 是作為線程通信的時候使用 |
int what | 定義的消息碼炮赦,一般用于設定消息的標志怜跑,辨別究竟是從哪里中發(fā)來的消息 |