異步消息處理線程啟動(dòng)后會(huì)進(jìn)入一個(gè)無限的循環(huán)體之中纲菌,每循環(huán)一次贾陷,從其內(nèi)部的消息隊(duì)列中取出一個(gè)消息,然后回調(diào)相應(yīng)的消息處理函數(shù)卿堂,執(zhí)行完成一個(gè)消息后則繼續(xù)循環(huán)束莫。若消息隊(duì)列為空,線程則會(huì)阻塞等待草描。
解析異步消息處理機(jī)制Handler
Android中的異步消息處理主要由四個(gè)部分組成览绿,Message、Handler陶珠、MessageQueue和Looper挟裂。
- Message
Message是在線程之間傳遞的消息,它可以在內(nèi)部攜帶少量的信息揍诽,用于在不同線程之間交換數(shù)據(jù)诀蓉。上一小節(jié)我們使用到了Message的what字段栗竖,初次之外還可以使用arg1和arg2字段來攜帶一些整型數(shù)據(jù),使用obj字段攜帶一個(gè)Object對(duì)象渠啤。 - Handler
Handler顧名思義也就是處理者的意思狐肢,它主要是用于發(fā)送和處理消息的。發(fā)送消息一般是使用Handler的sendMessage()方法沥曹,而發(fā)出的消息經(jīng)過一系列地輾轉(zhuǎn)處理后份名,最終會(huì)傳遞到Handler的handleMessage()方法中。 - MessageQueue
MessageQueue是消息隊(duì)列的意思妓美,它主要是用于存放所有的Handler發(fā)送的消息僵腺。這部分消息會(huì)一直存在于消息隊(duì)列中,等待被處理壶栋。每個(gè)線程中只有一個(gè)MessageQueue對(duì)象菱鸥。
4.Looper
Looper是每個(gè)線程中的MessageQueue的管家近上,調(diào)用Looper的loop()方法后撮珠,就會(huì)進(jìn)入到一個(gè)無限循環(huán)當(dāng)中攀操,然后每當(dāng)發(fā)現(xiàn)MessageQueue中存在一條消息,就會(huì)將它取出毙玻,并傳遞到Handler的handleMessage()方法中豌蟋。每個(gè)線程中也只會(huì)有一個(gè)Looper對(duì)象。
首先需要在主線程當(dāng)中創(chuàng)建一個(gè)Handler對(duì)象桑滩,并重寫handleMessage()方法梧疲。然后當(dāng)子線程中需要進(jìn)行UI操作時(shí),就創(chuàng)建一個(gè)Message對(duì)象施符,并通過Handler將這條信息發(fā)送出去往声。之后這條消息會(huì)被添加到MessageQueue的隊(duì)列中等待被處理,而Looper則會(huì)一直嘗試從MessageQueue中取出待處理消息戳吝,最后分發(fā)回Handler的handleMessage()方法中浩销。由于Handler是在主線程中創(chuàng)建的,所以此時(shí)handleMessage()方法中的代碼也會(huì)在主線程運(yùn)行听哭,于是我們?cè)谶@里就可以安心地進(jìn)行UI操作了慢洋。
Android異步消息處理機(jī)制讓你深入理解Looper、Handler陆盘、Message三者關(guān)系
Looper主要是prepare()和loop()兩個(gè)方法
一個(gè)線程中只有一個(gè)Looper實(shí)例
looper方法必須在prepare方法之后運(yùn)行普筹。
Looper主要作用:
1、 與當(dāng)前線程綁定隘马,保證一個(gè)線程只會(huì)有一個(gè)Looper實(shí)例太防,同時(shí)一個(gè)Looper實(shí)例也只有一個(gè)MessageQueue。
2酸员、 loop()方法蜒车,不斷從MessageQueue中去取消息讳嘱,交給消息的target屬性的dispatchMessage去處理。好了酿愧,我們的異步消息處理線程已經(jīng)有了消息隊(duì)列(MessageQueue)沥潭,也有了在無限循環(huán)體中取出消息的哥們,現(xiàn)在缺的就是發(fā)送消息的對(duì)象了嬉挡,于是乎:Handler登場(chǎng)了钝鸽。
Handler
使用Handler之前,我們都是初始化一個(gè)實(shí)例庞钢,比如用于更新UI線程拔恰,我們會(huì)在聲明的時(shí)候直接初始化,或者在onCreate中初始化Handler實(shí)例
1基括、首先Looper.prepare()在本線程中保存一個(gè)Looper實(shí)例仁连,然后該實(shí)例中保存一個(gè)MessageQueue對(duì)象;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次阱穗,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)。
2使鹅、Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無限循環(huán)揪阶,不端從MessageQueue的實(shí)例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法患朱。
3鲁僚、Handler的構(gòu)造方法,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例裁厅,進(jìn)而與Looper實(shí)例中的MessageQueue想關(guān)聯(lián)冰沙。
4、Handler的sendMessage方法执虹,會(huì)給msg的target賦值為handler自身拓挥,然后加入MessageQueue中。
5袋励、在構(gòu)造Handler實(shí)例時(shí)侥啤,我們會(huì)重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法茬故。
好了盖灸,總結(jié)完成,大家可能還會(huì)問磺芭,那么在Activity中赁炎,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創(chuàng)建呢钾腺,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中徙垫,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法
在主線程中可以直接創(chuàng)建Handler對(duì)象讥裤,而在子線程中需要先調(diào)用Looper.prepare()才能創(chuàng)建Handler對(duì)象。
1松邪、首先Looper.prepare()在本線程中保存一個(gè)Looper實(shí)例坞琴,然后該實(shí)例中保存一個(gè)MessageQueue對(duì)象;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次逗抑,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)剧辐。大家可能還會(huì)問,那么在Activity中邮府,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法荧关,為啥Handler可以成功創(chuàng)建呢,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中褂傀,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法
2忍啤、Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無限循環(huán),不端從MessageQueue的實(shí)例中讀取消息仙辟,然后回調(diào)msg.target.dispatchMessage(msg)方法同波。
3、Handler的構(gòu)造方法叠国,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例未檩,并與Looper實(shí)例中的MessageQueue相關(guān)聯(lián)。
4粟焊、Handler的sendMessage方法冤狡,會(huì)給msg的target賦值為handler自身,然后加入MessageQueue中项棠。
5悲雳、在構(gòu)造Handler實(shí)例時(shí),我們會(huì)重寫handleMessage方法香追,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法合瓢。
Handler+Looper+MessageQueue深入詳解
概述:Android使用消息機(jī)制實(shí)現(xiàn)線程間的通信,線程通過Looper建立自己的消息循環(huán)透典,MessageQueue是FIFO的消息隊(duì)列歪玲,Looper負(fù)責(zé)從MessageQueue中取出消息,并且分發(fā)到消息指定目標(biāo)Handler對(duì)象掷匠。Handler對(duì)象綁定到線程的局部變量Looper滥崩,封裝了發(fā)送消息和處理消息的接口。
實(shí)例:我們先介紹Android線程通訊的一個(gè)例子讹语,這個(gè)例子實(shí)現(xiàn)點(diǎn)擊按鈕之后從主線程發(fā)送消息"hello"到另外一個(gè)名為” CustomThread”的線程钙皮。
package
import
public class LooperThreadActivity extends Activity{
/** Called when the activity is first created. */
private final int MSG_HELLO = 0;
private Handler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new CustomThread().start(); //新建并啟動(dòng)CustomThread實(shí)例
findViewById(R.id.send_btn).setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {//點(diǎn)擊界面時(shí)發(fā)送消息
String str = "hello";
Log.d("Test", "MainThread is ready to send msg:" + str);
mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//發(fā)送消息到CustomThread實(shí)例
}
});
}
class CustomThread extends Thread{
@Override
public void run() {
//建立消息循環(huán)的步驟
Looper.prepare();//1、初始化Looper
mHandler = new Handler(){//2、綁定handler到CustomThread實(shí)例的Looper對(duì)象
public void handleMessage (Message msg) {//3短条、定義處理消息的方法
switch(msg.what) {
case MSG_HELLO:
Log.d("Test", "CustomThread receive msg:" + (String) msg.obj);
}
}
};
Looper.loop();//4导匣、啟動(dòng)消息循環(huán)
}
}
}
我們看到,為一個(gè)線程建立消息循環(huán)有四個(gè)步驟:
初始化Looper
綁定handler到CustomThread實(shí)例的Looper對(duì)象
定義處理消息的方法
啟動(dòng)消息循環(huán)
初始化Looper:Looper.prepare()
一個(gè)線程在調(diào)用Looper的靜態(tài)方法prepare()時(shí)茸时,這個(gè)線程會(huì)新建一個(gè)Looper對(duì)象贡定,并放入到線程的局部變量中,而這個(gè)變量是不和其他線程共享的可都。
在Looper的構(gòu)造函數(shù)中缓待,創(chuàng)建了一個(gè)消息隊(duì)列對(duì)象mQueue,此時(shí),調(diào)用Looper.prepare()的線程就建立起一個(gè)消息循環(huán)的對(duì)象(此時(shí)還沒開始進(jìn)行消息循環(huán))渠牲。
- 綁定handler到CustomThread實(shí)例的Looper對(duì)象:mHandler = new Handler()
Handler通過mLooper= Looper.myLooper();綁定到線程的局部變量Looper上去旋炒,同時(shí)Handler通過mQueue = mLooper.mQueue;獲得線程的消息隊(duì)列。此時(shí)签杈,Handler就綁定到創(chuàng)建此Handler對(duì)象的線程的消息隊(duì)列上了瘫镇。
定義處理消息的方法:Override public void handleMessage(Message msg){}
子類需要覆蓋這個(gè)方法,實(shí)現(xiàn)接收到消息后的處理方法答姥。啟動(dòng)消息循環(huán):Looper.loop()
使用Thread+Handler實(shí)現(xiàn)非UI線程更新UI界面
public class ThreadhandlerActivity extends Activity{
private static final int MSG_SUCCESS = 0;
private static final int MSG_FAILURE = 1;
private Thread mThread;
private Handler mHandler = new Handler(){
public void handleMessage(Message msg){//此方法在ui線程運(yùn)行
switch(msg.what) {
case MSG_SUCCESS:
mImageView.setImageBitmap((Bitmap) msg.obj);//imageview顯示從網(wǎng)絡(luò)獲取到的logo
Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show();
break;
case MSG_FAILURE:
Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show();
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView= (ImageView) findViewById(R.id.imageView);//顯示圖片的ImageView
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mThread == null) {
mThread = new Thread(runnable);
mThread.start();//線程啟動(dòng)
}
else {
Toast.makeText(getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show();
}
}
});
}
Runnable runnable = new Runnable() {
@Override
public void run() {//run()在新的線程中運(yùn)行
HttpClient hc = new DefaultHttpClient();
HttpGet hg = new HttpGet("http://www.oschina.net/img/logo.gif");//獲取oschina的logo
final Bitmap bm;
try {
HttpResponse hr = hc.execute(hg);
bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
} catch (Exception e) {
mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//獲取圖片失敗
return;
}
mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//獲取圖片成功铣除,向ui線程發(fā)送MSG_SUCCESS標(biāo)識(shí)和bitmap對(duì)象
// mImageView.setImageBitmap(bm); //出錯(cuò)!不能在非ui線程操作ui元素
// mImageView.post(new Runnable() {//另外一種更簡(jiǎn)潔的發(fā)送消息給ui線程的方法鹦付。
//
// @Override
// public void run() {//run()方法會(huì)在ui線程執(zhí)行
// mImageView.setImageBitmap(bm);
// }
// });
}
};
}
Android開發(fā)中Handler的經(jīng)典總結(jié)
當(dāng)應(yīng)用程序啟動(dòng)時(shí)通孽,Android首先會(huì)開啟一個(gè)主線程(也叫UI線程),主線程為管理界面中的UI控件睁壁,進(jìn)行事件分發(fā)。
- Handler定義
主要接收子線程發(fā)送的數(shù)據(jù)互捌,并用此數(shù)據(jù)配合主線程更新UI潘明。
Android主線程是線程不安全的,更新UI只能在主線程中更新秕噪,子線程中操作是危險(xiǎn)的钳降。
Handler運(yùn)行在主線程中,它與子線程可以通過Message對(duì)象來傳遞數(shù)據(jù)腌巾,這個(gè)時(shí)候遂填,Handler就承擔(dān)著接收子線程傳過來的(子線程用sendMessage()方法傳遞)Message對(duì)象,(里面包含數(shù)據(jù))澈蝙,把這些消息放入主線程隊(duì)列中吓坚,配合主線程進(jìn)行更新UI。
- Handler一些特點(diǎn)
Handler可以分發(fā)Message對(duì)象和Runnable對(duì)象到主線程中灯荧,每個(gè)Handler實(shí)例礁击,都會(huì)綁定到創(chuàng)建它的線程池中(一般是位于主線程),它有兩個(gè)作用:
(1)安排消息或Runnable在某個(gè)主線程中某個(gè)地方執(zhí)行;
(2)安排一個(gè)動(dòng)作在不同的線程中執(zhí)行哆窿。
Handler中分發(fā)消息的一些方法
post(Runnable)
postAtTime(Runnable链烈,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message挚躯,long)
以上post類方法允許你安排一個(gè)Runnable對(duì)象到主線程隊(duì)列中强衡,sendMessage類方法,允許你安排一個(gè)帶數(shù)據(jù)的Message對(duì)象到隊(duì)列中码荔,等待更新漩勤。
- Handler實(shí)例
子類需要繼承Handler類,并重寫handleMessage(Message msg)方法目胡,用于接收線程數(shù)據(jù)锯七。
public class MyHandlerActivity extends Activity {
Button button;
MyHandler myHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handlertest);
button = (Button) findViewById(R.id.button);
myHandler = new MyHandler();
//當(dāng)創(chuàng)建一個(gè)新的Handler實(shí)例時(shí),它會(huì)綁定到當(dāng)前線程和消息的隊(duì)列中誉己,開始分發(fā)數(shù)據(jù)
//Handler有兩個(gè)作用:(1)定時(shí)執(zhí)行Message和Runnable對(duì)象
//(2):讓一個(gè)動(dòng)作眉尸,在不同的線程中執(zhí)行。
//它安排消息巨双,用以下方法
//post(Runnable)
//postAtTime(Runnable, long)
// postDelayed(Runnable, long)
//sendEmptyMessage(int)
//sendMessage(Message)
//sendMessageAtTime(Message, long)
//sendMessageDelayed(Message, long)
//以上方法以post開頭的允許你處理Runnable對(duì)象
//sendMessage()允許你處理Message對(duì)象(Message里可以包含數(shù)據(jù))
MyThread m = new MyThread();
new Thread(m).start();
}
/**
* 接收消息噪猾,處理消息,此Handler會(huì)與當(dāng)前主線程一塊運(yùn)行
**/
class MyHandler extends Handler {
public MyHandler(){
}
public MyHandler(Looper L){
super(L);
}
//子類必須重寫此方法筑累,接收數(shù)據(jù)
@Override
public void handleMessage(Message msg){
// TODO Auto-generated method stub
Log.d("MyHandler","handleMessage....");
super.handleMessage(msg);
//此處可以更新UI
Bundle b = msg.getDada();
String color = b.getString("color");
MyHandlerActivity.this.button.append(color);
}
}
class MyThread implements Runnable{
public void run(){
try{
Thread.sleep(10000);
} catch(InterruptedException e){
//TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("thread....","mThread....");
Message msg = new Message();
Bundle b = new Bundle(); //存放數(shù)據(jù)
b.putString("color","我的");
msg.setData(b);
MyHandlerActivity.this.myHandler.sendMessage(msg);//向Handler發(fā)送消息袱蜡,更新UI
}
}
}
一個(gè)Handler允許你發(fā)送和處理消息以及與一個(gè)線程的消息隊(duì)列相關(guān)的Runnable對(duì)象。每個(gè)Handler實(shí)例都和單個(gè)線程以及該線程的消息隊(duì)列有關(guān)慢宗。當(dāng)你創(chuàng)建了一個(gè)新handler,它就會(huì)和創(chuàng)建它的線程/消息隊(duì)列綁定坪蚁,在那以后,它就會(huì)傳遞消息以及runnable對(duì)象給消息隊(duì)列镜沽,然后執(zhí)行它們敏晤。
需要使用Handler有兩大主要的原因:
(1)在將來的某個(gè)時(shí)間點(diǎn)調(diào)度處理消息和runnable對(duì)象;
(2)將需要執(zhí)行的操作放到其他線程之中缅茉,而不是自己的
若在主線程中實(shí)例化一個(gè)Handler對(duì)象嘴脾,例如:
Handler mHandler = new Handler();
此時(shí)它并沒有新派生一個(gè)線程來執(zhí)行此Handler,而是將此Handler附加在主線程上蔬墩,故此時(shí)若你在Handler中執(zhí)行耗時(shí)操作的話译打,還是會(huì)彈出ANR對(duì)話框!
創(chuàng)建Handler
- 使用默認(rèn)的構(gòu)造方法:new Handler()
- 使用帶參的構(gòu)造方法拇颅,參數(shù)是一個(gè)Runnable對(duì)象或者回調(diào)對(duì)象
Handler只是簡(jiǎn)單地往消息隊(duì)列中發(fā)送消息而已
它們有更方便的方法可以幫助與UI線程通信奏司。
在Android,線程分為有消息循環(huán)的線程和沒有消息循環(huán)的線程樟插,有消息循環(huán)的線程一般都會(huì)有一個(gè)Looper结澄。我們的主線程(UI線程)就是一個(gè)消息循環(huán)的線程哥谷。針對(duì)這種消息循環(huán)的機(jī)制,我們引入了一個(gè)新的機(jī)制Handler麻献,我們有消息循環(huán)们妥,就要往消息循環(huán)里面發(fā)送相應(yīng)的消息,自定義消息一般都會(huì)有自己對(duì)應(yīng)的處理勉吻,消息的發(fā)送和清除监婶,消息的處理,把這些都封裝在Handler里面齿桃,注意Handler只是針對(duì)那些有Looper的線程惑惶,不管是UI線程還是子線程,只要你有Looper短纵,我就可以往你的消息隊(duì)列里面添加?xùn)|西带污,并做相應(yīng)的處理。
如果在子線程里面新建handler香到,就會(huì)出現(xiàn)錯(cuò)誤鱼冀,原因就是一個(gè)線程對(duì)應(yīng)一個(gè)或零個(gè)Looper和MessageQueue。handler是一種消息機(jī)制悠就,而子線程的啟用默認(rèn)是沒有Looper對(duì)象的(主線程有)千绪,所以在子線程使用Handler對(duì)象的時(shí)候,要先使用Looper.prepare()梗脾,啟用一個(gè)Looper荸型,然后新建Handler對(duì)象,再使用Looper.loop().
至此子線程就有了自己的Looper炸茧,可以接收和處理信息瑞妇。
android.os.Looper:
Looper用于封裝了android線程中的消息循環(huán),默認(rèn)情況下一個(gè)線程是不存在消息循環(huán)(message loop)的梭冠,需要調(diào)用Looper.prepare()來給線程創(chuàng)建一個(gè)消息循環(huán)辕狰,調(diào)用Looper.loop()來使消息循環(huán)起作用,從消息隊(duì)列里取消息妈嘹,處理消息。
注:寫在Looper.loop()之后的代碼不會(huì)被立即執(zhí)行绍妨,當(dāng)調(diào)用后mHandler.getLooper().quit()后润脸,loop才會(huì)中止,其后的代碼才能得以運(yùn)行他去。Looper對(duì)象通過MessageQueue來存放消息和事件毙驯。一個(gè)線程只能有一個(gè)Looper,對(duì)應(yīng)一個(gè)MessageQueue灾测。
一個(gè)典型的Looper Thread實(shí)現(xiàn):
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
android.os.Handler:
Handler用于跟線程綁定爆价,來向線程的消息循環(huán)里面發(fā)送消息、接收消息并處理消息。
通過以下函數(shù)來向線程發(fā)送消息或Runnable:
1.post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long)铭段;
當(dāng)線程接收到Runnable對(duì)象后即立即或一定延遲調(diào)用骤宣。
2.sendEmptyMessage(int), sendMessage(Message)
, sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)。
當(dāng)線程接受到這些消息后序愚,根據(jù)你的Handler重構(gòu)的handleMessage(Message)根據(jù)接收到的消息來進(jìn)行處理憔披。
一個(gè)Activity主線程中可以有多個(gè)Handler對(duì)象,但MessageQueue是只有一個(gè)爸吮,對(duì)應(yīng)的Looper也是只有一個(gè)芬膝。
Looper類的靜態(tài)成員函數(shù)prepareMainLooper是專門應(yīng)用程序的主線程調(diào)用的,為了讓其它地方能夠方便地通過Looper類的getMainLooper函數(shù)來獲得應(yīng)用程序主線程中的消息循環(huán)對(duì)象形娇。
Android Handler詳細(xì)使用方法實(shí)例
handler使用例1.
這個(gè)例子是最簡(jiǎn)單的介紹handler使用的锰霜,是將handler綁定到它所建立的線程中。
單擊Start按鈕桐早,程序會(huì)開始啟動(dòng)線程癣缅,并且線程程序完成后延時(shí)1s會(huì)繼續(xù)啟動(dòng)該線程,每次線程的run函數(shù)中完成對(duì)界面輸出nUpdateThread…文字勘畔,不停的運(yùn)行下去所灸,當(dāng)單擊End按鈕時(shí),該線程就會(huì)停止炫七,如果繼續(xù)單擊Start爬立,則文字又開始輸出了。
MainActivity.java;
主要代碼
public class MainActivity extends Activity{
//使用handler時(shí)首先要?jiǎng)?chuàng)建一個(gè)handler對(duì)象
Handler handler = new Handler();
//要用Handler來處理多線程可以使用Runnable接口万哪,這里先定義該接口
//線程中運(yùn)行該接口的run函數(shù)
Runnable update_thread = new Runnable()
{
public void run()
{
handler.postDelayed(update_thread,1000);
}
};
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState );
...
}
//btn監(jiān)聽實(shí)現(xiàn)
//調(diào)用handler的post方法侠驯,將要執(zhí)行的線程對(duì)象添加到隊(duì)列當(dāng)中
//將線程接口立刻送到線程隊(duì)列
handler.post(update_thread);
//將接口從線程隊(duì)列中移除
handler.removeCallbacks(update_thread);
}
post方法雖然發(fā)送的是一個(gè)實(shí)現(xiàn)了Runnable接口的類對(duì)象奕巍,但是它并非創(chuàng)建了一個(gè)新線程吟策,而是執(zhí)行了該對(duì)象中的run方法。也就是說的止,整個(gè)run中的操作和主線程處于同一個(gè)線程檩坚。
為了解決這個(gè)問題,就需要使得handler綁定到一個(gè)新開啟線程的消息隊(duì)列上诅福,在這個(gè)處于另外線程上的消息隊(duì)列中處理傳過來的Runnable對(duì)象和消息匾委。
這個(gè)例子將學(xué)會(huì)怎樣不使用runnable來啟動(dòng)一個(gè)線程,而是用HandlerThread的looper來構(gòu)造一個(gè)handler氓润,然后該handler自己獲得消息赂乐,并傳遞數(shù)據(jù),然后又自己處理消息咖气,當(dāng)然這是在另一個(gè)線程中完成的挨措。
消息結(jié)構(gòu)中傳遞簡(jiǎn)單的整型可以采用它的參數(shù)arg1和arg2挖滤,或者傳遞一些小的其它數(shù)據(jù),可以用它的object浅役,該object可以是任意的對(duì)象斩松。當(dāng)需要傳送比較大的數(shù)據(jù)是,可以使用消息的setData方法担租,該方法需要傳遞一個(gè)Bundle的參數(shù)砸民。Bundle中存放的是鍵值對(duì)的map,只是它的鍵值類型和數(shù)據(jù)類型比較固定而已奋救。
public class HandleTest2 extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState );
setContentView(R.layout.main);
//生成一個(gè)HandlerThread對(duì)象
HandlerThread handlerThread = new HandlerThread("handler_thread");
//在使用HandlerThread的getLooper()方法之前岭参,必須先調(diào)用該類的start(),同時(shí)開啟一個(gè)新線程尝艘;
handlerThread.start();
//將由HandlerThread獲取的Looper傳遞給Handler對(duì)象演侯,即由處于另外線程的Looper代替handler初始化時(shí)默認(rèn)綁定的消息隊(duì)列來處理消息。
//HandlerThread顧名思義就是好可以處理消息循環(huán)的線程背亥,它是一個(gè)擁有Looper的線程秒际,可以處理消息循環(huán);其實(shí)與其說Handler和一個(gè)線程綁定狡汉,倒不如說Handler和Looper是一一對(duì)應(yīng)的娄徊。
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
//將msg發(fā)送到目標(biāo)對(duì)象,所謂的目標(biāo)對(duì)象盾戴,就是生成該msg對(duì)象的handler對(duì)象
Bundle b = new Bundle();
b.putInt("age",20);
b.putString("name","Jhon");
msg.setData(b);
//將msg發(fā)送到自己的handler中
msg.sendToTarget(); //將msg發(fā)送到myHandler
}
//定義類
class MyHandler extends Handler{
public MyHandler(){
}
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg){
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
}
}
}
這樣寄锐,當(dāng)使用sendMessage方法傳遞消息或者使用post方法傳遞Runnable對(duì)象時(shí),就會(huì)把它們傳遞到與handler對(duì)象綁定的處于另外一個(gè)線程的消息隊(duì)列中尖啡,它們將在另外的消息隊(duì)列中被處理橄仆。而主線程還會(huì)在發(fā)送完成時(shí)候繼續(xù)進(jìn)行,不會(huì)影響當(dāng)前的操作衅斩。
這里需要注意盆顾,這里用到的多線程并非由Runnable對(duì)象開啟的,而是ThreadHandler對(duì)象開啟的畏梆。Runnable對(duì)象只是作為一個(gè)封裝了操作的對(duì)象被傳遞您宪,并未產(chǎn)生新線程。
另外再?gòu)?qiáng)調(diào)一遍奠涌,在UI線程(主線程)中:
mHandler=new Handler();
mHandler.post(new Runnable(){
void run(){
//執(zhí)行代碼..
}
});
這個(gè)線程其實(shí)是在UI線程之內(nèi)運(yùn)行的宪巨,并沒有新建線程。
常見的新建線程的方法是:
Thread thread = new Thread();
thread.start();
HandlerThread thread = new HandlerThread("string");
thread.start();
Handler+Looper+MessageQueue深入詳解
1.主線程給自己發(fā)送Message
Looper looper = Looper.getMainLooper()铣猩;//獲取主線程的Looper對(duì)象
//這里以主線程的Looper對(duì)象創(chuàng)建了handler揖铜,
//所以茴丰,這個(gè)handler發(fā)送的Message會(huì)被傳遞給主線程的MessageQueue达皿。
handler = new MyHandler(looper);
handler.removeMessages(0);
//構(gòu)建Message對(duì)象
//第一個(gè)參數(shù):是自己指定的message代號(hào)天吓,方便在handler選擇性地接收
//第二三個(gè)參數(shù)沒有什么意義
//第四個(gè)參數(shù)需要封裝的對(duì)象
Message msg = handler.obtainMessage(1,1,1,"someword");
handler.sendMessage(msg);//發(fā)送消息
2.其他線程給主線程發(fā)送Message
public class MainActivity extends Activity {
private Handler handler;
//可以看出這里啟動(dòng)了一個(gè)線程來操作消息的封裝和發(fā)送的工作
new MyThread().start();
//加載一個(gè)線程類
class Mythread extends Thread{
public void run(){
Looper looper = Looper.getMainLooper();//主線程的Looper對(duì)象
//這里以主線程的Looper對(duì)象創(chuàng)建了handler
//所以,這個(gè)handler發(fā)送的Message會(huì)被傳遞給主線程的MessageQueue
handler = new MyHandler(looper);
//構(gòu)建Message對(duì)象
//第一個(gè)參數(shù):是自己指定的message代號(hào)峦椰,方便在handler選擇性地接收
//第二三個(gè)參數(shù)沒有什么意義
//第四個(gè)參數(shù)需要封裝的對(duì)象
Message msg = handler.obtainMessage(1,1,1,"其他線程發(fā)消息了");
handler.sendMessage(msg); //發(fā)送消息
}
}
}
- 主線程給其他線程發(fā)送Message
public class MainActivity extends Activity {
private Button btnTest;
private TextView textView;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnTest = (Button)this.findViewById(R.id.btn_01);
textView = (TextView)this.findViewById(R.id.view_01);
//啟動(dòng)線程
new MyThread().start();
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//這里handler的實(shí)例化在線程中
//線程啟動(dòng)的時(shí)候就已經(jīng)實(shí)例化了
Message msg = handler.obtainMessage(1,1,1,"主線程發(fā)送的消息");
handler.sendMessage(msg);
}
});
}
class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
super.handleMessage(msg);
textView.setText("我是主線程的Handler龄寞,收到了消息:"+ (String)msg.obj);
}
}
class MyThread extends Thread{
public void run(){
Looper.prepare(); //創(chuàng)建該線程的Looper對(duì)象,用于接收消息
//注意了:這里的handler是定義在主線程中的哦汤功,呵呵物邑,
//前面看到直接使用了handler對(duì)象,是不是在找滔金,在什么地方實(shí)例化的呢色解?
//現(xiàn)在看到了吧?餐茵?科阎?呵呵,開始的時(shí)候?qū)嵗涣朔拮澹驗(yàn)樵摼€程的Looper對(duì)象
//還不存在呢÷啾浚現(xiàn)在可以實(shí)例化了
//這里L(fēng)ooper.myLooper()獲得的就是該線程的Looper對(duì)象了
handler = new ThreadHandler(Looper.myLooper());
//這個(gè)方法,有疑惑嗎道批?
//其實(shí)就是一個(gè)循環(huán)错英,循環(huán)從MessageQueue中取消息。
//不經(jīng)常去看看隆豹,你怎么知道你有新消息呢椭岩??噪伊?
Looper.loop();
}
//定義線程類中的消息處理類
class ThreadHandler extends Handler{
public ThreadHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
//這里對(duì)該線程中的MessageQueue中的Message進(jìn)行處理
//這里我們?cè)俜祷亟o主線程一個(gè)消息
handler = new MyHandler(Looper.getMainLooper());
Message msg2 = handler.obtainMessage(1,1,1,"子線程收到:"+(String)msg.obj);
handler.sendMessage(msg2);
}
}
}
}
多個(gè)任務(wù)的話簿煌,還有一個(gè)問題,這里是并行還是串行執(zhí)行鉴吹?
Android 中Message,MessageQueue,Looper,Handler詳解+實(shí)例
http://tanghaibo001.blog.163.com/blog/static/9068612020111287218197/
Android 異步消息處理機(jī)制 讓你深入理解 Looper姨伟、Handler、Message三者關(guān)系
http://blog.csdn.net/lmj623565791/article/details/38377229?utm_source=tuicool&utm_medium=referral