Handler是什么?
Handler是Android提供的一套用來更新UI的機(jī)制,也是一套消息處理機(jī)制,可以通過它發(fā)送消息绪励,也可以通過它處理消息。
所有Activity生命周期回調(diào)的方法唠粥,都是通過handler機(jī)制去發(fā)送消息的疏魏,然后根據(jù)不同的消息(message)做相應(yīng)的分支處理。例如發(fā)送一個消息給 Framework晤愧,告知需要調(diào)用onCreate()或onDestory()方法大莫。實(shí)際上在 Framework 當(dāng)中,Activity的交互大部分都是用AMS(Activity Manager Service)做處理的养涮。整個應(yīng)用程序的核心的一個類就是 Activity Thread葵硕,Activity Thread就是通過handler機(jī)制接收到 Activity Manager Service發(fā)送的有關(guān)Activity生命周期的管理的消息(比如啟動)眉抬。
為什么要使用Handler贯吓?
Android在設(shè)計的時候,就封裝了一套消息的創(chuàng)建蜀变、傳遞悄谐、處理機(jī)制,如果不遵循這樣的機(jī)制库北,就沒有辦法更新UI信息爬舰,并且會拋出異常信息们陆。這樣的機(jī)制便包含Handler機(jī)制。
我們需要把這些耗時的操作情屹,放在一個子線程中坪仇,因?yàn)樽泳€程涉及到UI更新,Android主線程是線程不安全的垃你,也就是說椅文,更新UI只能在主線程中更新,子線程中操作是危險的惜颇。這個時候皆刺,Handler就出現(xiàn)了。來解決這個復(fù)雜的問題 凌摄,由于Handler運(yùn)行在主線程中(UI線程中)羡蛾,它與子線程可以通過Message對象來傳遞數(shù)據(jù), 這個時候锨亏,Handler就承擔(dān)著接受子線程傳過來的(子線程用sedMessage()方法傳遞)Message對象(里面包含數(shù)據(jù))痴怨,把這些消息放入主線程隊列中,配合主線程進(jìn)行更新UI屯伞。
Handler主要接受子線程發(fā)送的數(shù)據(jù)腿箩,并用此數(shù)據(jù)配合主線程更新UI。
當(dāng)應(yīng)用程序啟動時劣摇,Android首先會開啟一個主線程 (也就是UI線程) 珠移,主線程為管理界面中的UI控件,進(jìn)行事件分發(fā)末融,比如說钧惧, 你要是點(diǎn)擊一個 Button ,Android會分發(fā)事件到Button上勾习,來響應(yīng)你的操作浓瞪。如果此時需要一個耗時的操作,例如: 聯(lián)網(wǎng)讀取數(shù)據(jù)巧婶,或者讀取本地較大的一個文件的時候乾颁,你不能把這些操作放在主線程中,如果你放在主線程中的話艺栈,界面會出現(xiàn)假死現(xiàn)象英岭,如果5秒鐘還沒有完成的話,會收到Android系統(tǒng)的一個錯誤提示 "強(qiáng)制關(guān)閉"湿右。
- 我們要創(chuàng)建一個handler的時候诅妹,它會和默認(rèn)的一個線程進(jìn)行綁定,而這個默認(rèn)的線程當(dāng)中就有一個MessageQueue(消息隊列)。
- handler的兩個用途:(1)定時發(fā)送一個Messages或者Runnables對象吭狡;(2)可以在一個線程當(dāng)中去處理并執(zhí)行一個Action的動作尖殃。
- 主線程運(yùn)行一個消息隊列,并管理著一些頂級的應(yīng)用對象(top-level application objects)划煮,包括Activity送丰、Broadcast Receiver等等,這些對象默認(rèn)都是創(chuàng)建在Activity Thread(也就是我們常說的UI線程或者主線程)當(dāng)中弛秋。
下面圖是通過postDelayed來更新圖片
Handler的示例蚪战,實(shí)現(xiàn)圖片輪播。
代碼實(shí)現(xiàn) - 在主布局中定義一個ImageView控件铐懊。
- 在 MainActivity 中創(chuàng)建并初始化ImageView邀桑,定義一個圖片數(shù)組 images 和數(shù)組下標(biāo)索引 index,創(chuàng)建一個Handler對象科乎。
- 創(chuàng)建一個內(nèi)部類 MyRunnable 實(shí)現(xiàn) Runnable 接口壁畸,重寫 run() 方法:
public void run() {
index++;
index = index%3; // 圖片輪播(一般是通過ViewPager實(shí)現(xiàn)圖片輪播)
imageView.setImageResource(images[index]);
handler.postDelayed(myRunnable,1000); // 每隔一秒換一次圖片
} - 在onCreate()方法中調(diào)用handler,也就是在主線程中調(diào)用handler:
handler.postDelayed(myRunnable,1000);
使用“Message”方式“發(fā)送消息”茅茂,使“Handler處理消息捏萍,并更新UI”
1.定義Handler,并且實(shí)例化空闲,使用默認(rèn)構(gòu)造函數(shù)即可令杈。
2.重寫handlerMessage方法。
private Handler msgHandler=new Handler(){
//定義handler碴倾,重寫處理message方法逗噩。當(dāng)該handler發(fā)送消息的時候,這個方法會被執(zhí)行跌榔。
public void handleMessage(android.os.Message msg) {
//msg為當(dāng)有sendMessage方法調(diào)用時异雁,傳過來的Message對象。
mTextView.setText("msg:"+msg.arg1);
};
};
3.定義Message對象僧须。其中纲刀,Message提供了三個公共變量。arg1担平,arg2示绊,obj,可以將消息放入其中暂论,作為消息信息面褐。然后發(fā)送消息即可
new Thread()
{
public void run() {
Message message=msgHandler.obtainMessage();//從Handler對象中獲取Message對象,而不是自己new空另,這樣效率高盆耽。
message.arg1=1;
message.sendToTarget(); //對于從handler對象中獲取的message蹋砚,可以直接使用該方法發(fā)送消息扼菠。
//msgHandler.sendMessage(message); //發(fā)送消息
};
}.start();
知識拓展:
1摄杂。實(shí)例化Handler時,可以使用帶一個Callback接口參數(shù)的構(gòu)造函數(shù)循榆。
其中Callback有一個未實(shí)現(xiàn)的方法析恢。 這個方法有一個返回值(boolean)
2.當(dāng)該Handler收到消息時,首先會調(diào)用Callback中的消息處理方法秧饮。
2.1 如果返回值為false映挂,消息不會被截斷。Handler依然可以處理該消息盗尸。
2.2 如果返回值為true柑船,消息將會被截斷。Handler中的處理消息方法不會被執(zhí)行泼各。
handler鞍时、looper、messagequeue扣蜻、message四者可以這樣理解:
handler:工人逆巍;
looper:傳送帶移動的動力;
messagequeue:傳送帶莽使;
message:傳送帶上面的產(chǎn)品锐极。
工人(handler)把自己的產(chǎn)品(message)放在傳送帶(messagequeue)尾部,在動力(looper)作用下芳肌,傳送帶向前移動灵再,最終產(chǎn)品到達(dá)傳送帶頭部,被取出來處理(handmessage())亿笤。
先要有個概念檬嘀。
1、handler 消息處理器责嚷,負(fù)責(zé)處理消息鸳兽。
2、Message 消息罕拂,包含消息id揍异,被處理的對象。
3爆班、MessageQueue 消息隊列衷掷,存放Handler發(fā)送過來的Message
4、looper 消息泵柿菩,不間斷的從MessageQueue消息隊列中抽取消息戚嗅。
簡單的比喻looper就是水泵,MessageQueue儲水的池塘,Message就是水懦胞,Handler就是操作的人替久。
主線程和子線程之間如何相互通信:
- 子線程向主線程發(fā)送消息,是通過調(diào)用主線程的Handler躏尉,發(fā)送信息給主線程的Looper,該Hnadler綁定了主線程的Looper蚯根。
- 主線程向子線程發(fā)送消息,是通過調(diào)用子線程的Handler胀糜,發(fā)送信息給子線程的Looper,因此必須有子線程的Looper颅拦,為了防止Looper沒有初始化,所以通過HandlerThread類教藻,來保證子線程的Looper再被主線程調(diào)用時已經(jīng)初始化距帅。
1.創(chuàng)建主線程的handler,并向子線程發(fā)送消息:
//主線程的handler
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
Message message = new Message();
threadHandler.sendMessageDelayed(message, 1000);//向子線程發(fā)送消息
};
};
2.創(chuàng)建子線程的handler,向主線程發(fā)送消息括堤,要關(guān)聯(lián)一個threadHandler,threadHandler.getLooper()得到一個looper對象
HandlerThread handlerThread = new HandlerThread("handler thread");
handlerThread.start();//不要忘記調(diào)用start方法锥债!
//子線程的handler
threadHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {//處理消息
Message message = new Message();
handler.sendMessageDelayed(message, 1000);
}
};
更新UI的四種方法:
tv指一個TextView;
先在onCreate()方法中新建一個線程:
new Thread() {
public void run() {
try {
Thread.sleep(2000);
方法()痊臭;
} catch (InterruptedException e) {
e.printStackTrace();
}
} }.start();
方法一:private void handle1() {
handler.post(new Runnable() {
@Override
public void run() {
tv.setText("First Way");
}
});
}
方法二:
先創(chuàng)建一個handler:
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
tv.setText("Second Way!");
};
};
然后
private void handle2() {
handler.sendEmptyMessage(1);
}
方法三:
private void updateUI() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
tv.setText("Third Way");
}
});
}
方法四:
private void viewUI() {
tv.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
tv.setText("Fourth Way");
}
});
}