深入Android Handler與線程間通信ITC的詳解
更新時(shí)間:2013年05月21日 12:15:46 作者:
本篇文章是對Android的Handler與線程間通信ITC進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
在《Android Handler之消息循環(huán)的深入解析》中談到了Handler是用于操作線程內(nèi)部的消息隊(duì)列,所以Handler可以用來線程間通信ITC,這種方式更加安全和高效,可以大大減少同步的煩惱,甚至都可以不用syncrhonized。
線程間通訊ITC
正常情況下函數(shù)調(diào)用棧都會(huì)生存在同一個(gè)線程內(nèi),想要把執(zhí)行邏輯交換到其他線程可以新建一個(gè)Thread,然后start()。另外一種方法就是用ITC,也即用消息隊(duì)列來實(shí)現(xiàn),線程需要把執(zhí)行邏輯交到其他線程時(shí)就向另外的線程的消息隊(duì)列發(fā)送一個(gè)消息,發(fā)送消息后函數(shù)就此結(jié)束返回,調(diào)用棧也停止。當(dāng)消息隊(duì)列中有了消息時(shí),線程會(huì)被喚醒來執(zhí)行處理消息,從而把執(zhí)行邏輯從一個(gè)線程轉(zhuǎn)到另外一個(gè)線程。這就實(shí)現(xiàn)了線程間的通信ITC,與進(jìn)行間通訊IPC有十分類似的思想。
通常的做法都是,在主線程創(chuàng)建一個(gè)Handler,然后在新建線程中使用此Handler與主線程通訊。因?yàn)橹骶€程的消息隊(duì)列已經(jīng)建好,所以直接創(chuàng)建Handler即可,新建的線程就可以直接使用。
有些情況,需要在多線程之間進(jìn)行通信,這就要為每個(gè)線程都創(chuàng)建MessageQueue和Handler,只要線程能訪問其他線程的Handler就可以與之通信。
要正確的創(chuàng)建Handler,因?yàn)镠andler要與線程綁定,所以在初始化Handler的時(shí)候就要注意:
如果給Handler指定Looper對象new Handler(Looper),那么此Handler便綁定到Looper對象所在的線程中,Handler的消息處理回調(diào)會(huì)在那個(gè)線程中執(zhí)行。
如果創(chuàng)建線程時(shí)不指定Looper對象,那么此Handler綁定到創(chuàng)建此Handler的線程內(nèi),消息回調(diào)處理會(huì)在那個(gè)線程中執(zhí)行,所以像下面的例子,如果這樣寫:
private class CookServer extends Thread {
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
....
}
};
那么,此mHandler會(huì)與創(chuàng)建此CookerServer的線程綁定,handleMessage也會(huì)運(yùn)行于其中。顯然,如果是主線程調(diào)用new CookServer(),那么mHandler其實(shí)是運(yùn)行在主線程中的。正確的寫法應(yīng)該是:
private class CookServer extends Thread {
public void run() {
Looper.prepare();
// or new Handler(Looper.myLooper())
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
....
}
};
HandlerThread
如果要在一個(gè)線程中使用消息隊(duì)列和Handler,Android API中已經(jīng)有封裝好了的一個(gè)類HandlerThread,這個(gè)類已經(jīng)做好了Looper的初始化工作,你需要做的就是重寫其onLooperPrepared()方法,在其中創(chuàng)建Handler:
private class DeliverServer extends HandlerThread {
private Handler mHandler;
public DeliverServer(String name) {
super(name);
}
@Override
public void onLooperPrepared() {
mHandler = new Handler(getLooper()) {
public void handleMessage(Message msg) {
.....
}
};
}
}
實(shí)例
此實(shí)例模擬了一個(gè)網(wǎng)絡(luò)訂餐系統(tǒng),客戶點(diǎn)擊“Submit order"來產(chǎn)生一個(gè)定單,主線程中負(fù)責(zé)收集定單,然后交由CookServer來制作,CookServer在制作完成后會(huì)交由DeliverServer來把食物運(yùn)送到客戶,至此一個(gè)定單完成,同時(shí)CookServer和DeliverServer會(huì)更新狀態(tài)。
/**
* How to attach an Handler to a Thread:
* If you specify Looper object to Handler, i.e. new Handler(Looper), then the handler is attached to the thread owning
* the Looper object, in which handleMessage() is executed.
* If you do not specify the Looper object, then the handler is attached to the thread calling new Handler(), in which
* handleMessage() is executed.
* In this example, for class CookServer or DeliverServer, if you write this way:
* private class CookServer extends Thread {
private Handler mHandler;
private Looper mLooper;
public CookServer() {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
....
}
start();
}
* then mHandler is attached to thread calling new CookServer(), which is the main thread, so mHandler.handleMessage() will
* be executed in main thread.
* To attach mHandler to its own thread, you must put it in run(), or after mLooper is created. For our example, providing
* mLooper or not won't matter, because new Handler() is called in run(), which is in a new thread.
*/
public class HandlerITCDemo extends ListActivity {
private static final int COOKING_STARTED = 1;
private static final int COOKING_DONE = 2;
private static final int DELIVERING_STARTED = 3;
private static final int ORDER_DONE = 4;
private ListView mListView;
private static final String[] mFoods = new String[] {
"Cubake",
"Donut",
"Eclaire",
"Gingerbread",
"Honeycomb",
"Ice Cream Sanwitch",
"Jelly Bean",
};
private ArrayList<String> mOrderList;
private TextView mGeneralStatus;
private Button mSubmitOrder;
private static Random mRandomer = new Random(47);
private int mOrderCount;
private int mCookingCount;
private int mDeliveringCount;
private int mDoneCount;
private Handler mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COOKING_STARTED:
mCookingCount++;
break;
case COOKING_DONE:
mCookingCount--;
break;
case DELIVERING_STARTED:
mDeliveringCount++;
break;
case ORDER_DONE:
mDeliveringCount--;
mDoneCount++;
default:
break;
}
mGeneralStatus.setText(makeStatusLabel());
}
};
private CookServer mCookServer;
private DeliverServer mDeliverServer;
@Override
protected void onDestroy() {
super.onDestroy();
if (mCookServer != null) {
mCookServer.exit();
mCookServer = null;
}
if (mDeliverServer != null) {
mDeliverServer.exit();
mDeliverServer = null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListView = getListView();
mOrderList = new ArrayList<String>();
mGeneralStatus = new TextView(getApplication());
mGeneralStatus.setText(makeStatusLabel());
mSubmitOrder = new Button(getApplication());
mSubmitOrder.setText("Submit order");
mSubmitOrder.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String order = mFoods[mRandomer.nextInt(mFoods.length)];
mOrderList.add(order);
mOrderCount = mOrderList.size();
mGeneralStatus.setText(makeStatusLabel());
setAdapter();
mCookServer.cook(order);
}
});
mListView.addHeaderView(mGeneralStatus);
mListView.addFooterView(mSubmitOrder);
setAdapter();
mCookServer = new CookServer();
mDeliverServer = new DeliverServer("deliver server");
}
private String makeStatusLabel() {
StringBuilder sb = new StringBuilder();
sb.append("Total: ");
sb.append(mOrderCount);
sb.append(" Cooking: ");
sb.append(mCookingCount);
sb.append(" Delivering: ");
sb.append(mDeliveringCount);
sb.append(" Done: ");
sb.append(mDoneCount);
return sb.toString();
}
private void setAdapter() {
final ListAdapter adapter = new ArrayAdapter<String>(getApplication(), android.R.layout.simple_list_item_1, mOrderList);
setListAdapter(adapter);
}
private class CookServer extends Thread {
private Handler mHandler;
private Looper mLooper;
public CookServer() {
start();
}
@Override
public void run() {
Looper.prepare();
mLooper = Looper.myLooper();
mHandler = new Handler(mLooper, new Handler.Callback() {
public boolean handleMessage(Message msg) {
new Cooker((String) msg.obj);
return true;
}
});
Looper.loop();
}
public void cook(String order) {
if (mLooper == null || mHandler == null) {
return;
}
Message msg = Message.obtain();
msg.obj = order;
mHandler.sendMessage(msg);
}
public void exit() {
if (mLooper != null) {
mLooper.quit();
mHandler = null;
mLooper = null;
}
}
}
private class Cooker extends Thread {
private String order;
public Cooker(String order) {
this.order = order;
start();
}
@Override
public void run() {
mMainHandler.sendEmptyMessage(COOKING_STARTED);
SystemClock.sleep(mRandomer.nextInt(50000));
mDeliverServer.deliver(order);
mMainHandler.sendEmptyMessage(COOKING_DONE);
}
}
private class DeliverServer extends HandlerThread {
private Handler mHandler;
public DeliverServer(String name) {
super(name);
start();
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper(), new Handler.Callback() {
public boolean handleMessage(Message msg) {
new Deliver((String) msg.obj);
return true;
}
});
}
public void deliver(String order) {
if (mHandler == null || getLooper() == null) {
return;
}
Message msg = Message.obtain();
msg.obj = order;
mHandler.sendMessage(msg);
}
public void exit() {
quit();
mHandler = null;
}
}
private class Deliver extends Thread {
private String order;
public Deliver(String order) {
this.order = order;
start();
}
@Override
public void run() {
mMainHandler.sendEmptyMessage(DELIVERING_STARTED);
SystemClock.sleep(mRandomer.nextInt(50000));
mMainHandler.sendEmptyMessage(ORDER_DONE);
}
}
}
線程間通訊ITC
正常情況下函數(shù)調(diào)用棧都會(huì)生存在同一個(gè)線程內(nèi),想要把執(zhí)行邏輯交換到其他線程可以新建一個(gè)Thread,然后start()。另外一種方法就是用ITC,也即用消息隊(duì)列來實(shí)現(xiàn),線程需要把執(zhí)行邏輯交到其他線程時(shí)就向另外的線程的消息隊(duì)列發(fā)送一個(gè)消息,發(fā)送消息后函數(shù)就此結(jié)束返回,調(diào)用棧也停止。當(dāng)消息隊(duì)列中有了消息時(shí),線程會(huì)被喚醒來執(zhí)行處理消息,從而把執(zhí)行邏輯從一個(gè)線程轉(zhuǎn)到另外一個(gè)線程。這就實(shí)現(xiàn)了線程間的通信ITC,與進(jìn)行間通訊IPC有十分類似的思想。
通常的做法都是,在主線程創(chuàng)建一個(gè)Handler,然后在新建線程中使用此Handler與主線程通訊。因?yàn)橹骶€程的消息隊(duì)列已經(jīng)建好,所以直接創(chuàng)建Handler即可,新建的線程就可以直接使用。
有些情況,需要在多線程之間進(jìn)行通信,這就要為每個(gè)線程都創(chuàng)建MessageQueue和Handler,只要線程能訪問其他線程的Handler就可以與之通信。
要正確的創(chuàng)建Handler,因?yàn)镠andler要與線程綁定,所以在初始化Handler的時(shí)候就要注意:
如果給Handler指定Looper對象new Handler(Looper),那么此Handler便綁定到Looper對象所在的線程中,Handler的消息處理回調(diào)會(huì)在那個(gè)線程中執(zhí)行。
如果創(chuàng)建線程時(shí)不指定Looper對象,那么此Handler綁定到創(chuàng)建此Handler的線程內(nèi),消息回調(diào)處理會(huì)在那個(gè)線程中執(zhí)行,所以像下面的例子,如果這樣寫:
復(fù)制代碼 代碼如下:
private class CookServer extends Thread {
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
....
}
};
那么,此mHandler會(huì)與創(chuàng)建此CookerServer的線程綁定,handleMessage也會(huì)運(yùn)行于其中。顯然,如果是主線程調(diào)用new CookServer(),那么mHandler其實(shí)是運(yùn)行在主線程中的。正確的寫法應(yīng)該是:
復(fù)制代碼 代碼如下:
private class CookServer extends Thread {
public void run() {
Looper.prepare();
// or new Handler(Looper.myLooper())
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
....
}
};
HandlerThread
如果要在一個(gè)線程中使用消息隊(duì)列和Handler,Android API中已經(jīng)有封裝好了的一個(gè)類HandlerThread,這個(gè)類已經(jīng)做好了Looper的初始化工作,你需要做的就是重寫其onLooperPrepared()方法,在其中創(chuàng)建Handler:
復(fù)制代碼 代碼如下:
private class DeliverServer extends HandlerThread {
private Handler mHandler;
public DeliverServer(String name) {
super(name);
}
@Override
public void onLooperPrepared() {
mHandler = new Handler(getLooper()) {
public void handleMessage(Message msg) {
.....
}
};
}
}
實(shí)例
此實(shí)例模擬了一個(gè)網(wǎng)絡(luò)訂餐系統(tǒng),客戶點(diǎn)擊“Submit order"來產(chǎn)生一個(gè)定單,主線程中負(fù)責(zé)收集定單,然后交由CookServer來制作,CookServer在制作完成后會(huì)交由DeliverServer來把食物運(yùn)送到客戶,至此一個(gè)定單完成,同時(shí)CookServer和DeliverServer會(huì)更新狀態(tài)。
復(fù)制代碼 代碼如下:
/**
* How to attach an Handler to a Thread:
* If you specify Looper object to Handler, i.e. new Handler(Looper), then the handler is attached to the thread owning
* the Looper object, in which handleMessage() is executed.
* If you do not specify the Looper object, then the handler is attached to the thread calling new Handler(), in which
* handleMessage() is executed.
* In this example, for class CookServer or DeliverServer, if you write this way:
* private class CookServer extends Thread {
private Handler mHandler;
private Looper mLooper;
public CookServer() {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
....
}
start();
}
* then mHandler is attached to thread calling new CookServer(), which is the main thread, so mHandler.handleMessage() will
* be executed in main thread.
* To attach mHandler to its own thread, you must put it in run(), or after mLooper is created. For our example, providing
* mLooper or not won't matter, because new Handler() is called in run(), which is in a new thread.
*/
public class HandlerITCDemo extends ListActivity {
private static final int COOKING_STARTED = 1;
private static final int COOKING_DONE = 2;
private static final int DELIVERING_STARTED = 3;
private static final int ORDER_DONE = 4;
private ListView mListView;
private static final String[] mFoods = new String[] {
"Cubake",
"Donut",
"Eclaire",
"Gingerbread",
"Honeycomb",
"Ice Cream Sanwitch",
"Jelly Bean",
};
private ArrayList<String> mOrderList;
private TextView mGeneralStatus;
private Button mSubmitOrder;
private static Random mRandomer = new Random(47);
private int mOrderCount;
private int mCookingCount;
private int mDeliveringCount;
private int mDoneCount;
private Handler mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COOKING_STARTED:
mCookingCount++;
break;
case COOKING_DONE:
mCookingCount--;
break;
case DELIVERING_STARTED:
mDeliveringCount++;
break;
case ORDER_DONE:
mDeliveringCount--;
mDoneCount++;
default:
break;
}
mGeneralStatus.setText(makeStatusLabel());
}
};
private CookServer mCookServer;
private DeliverServer mDeliverServer;
@Override
protected void onDestroy() {
super.onDestroy();
if (mCookServer != null) {
mCookServer.exit();
mCookServer = null;
}
if (mDeliverServer != null) {
mDeliverServer.exit();
mDeliverServer = null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListView = getListView();
mOrderList = new ArrayList<String>();
mGeneralStatus = new TextView(getApplication());
mGeneralStatus.setText(makeStatusLabel());
mSubmitOrder = new Button(getApplication());
mSubmitOrder.setText("Submit order");
mSubmitOrder.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String order = mFoods[mRandomer.nextInt(mFoods.length)];
mOrderList.add(order);
mOrderCount = mOrderList.size();
mGeneralStatus.setText(makeStatusLabel());
setAdapter();
mCookServer.cook(order);
}
});
mListView.addHeaderView(mGeneralStatus);
mListView.addFooterView(mSubmitOrder);
setAdapter();
mCookServer = new CookServer();
mDeliverServer = new DeliverServer("deliver server");
}
private String makeStatusLabel() {
StringBuilder sb = new StringBuilder();
sb.append("Total: ");
sb.append(mOrderCount);
sb.append(" Cooking: ");
sb.append(mCookingCount);
sb.append(" Delivering: ");
sb.append(mDeliveringCount);
sb.append(" Done: ");
sb.append(mDoneCount);
return sb.toString();
}
private void setAdapter() {
final ListAdapter adapter = new ArrayAdapter<String>(getApplication(), android.R.layout.simple_list_item_1, mOrderList);
setListAdapter(adapter);
}
private class CookServer extends Thread {
private Handler mHandler;
private Looper mLooper;
public CookServer() {
start();
}
@Override
public void run() {
Looper.prepare();
mLooper = Looper.myLooper();
mHandler = new Handler(mLooper, new Handler.Callback() {
public boolean handleMessage(Message msg) {
new Cooker((String) msg.obj);
return true;
}
});
Looper.loop();
}
public void cook(String order) {
if (mLooper == null || mHandler == null) {
return;
}
Message msg = Message.obtain();
msg.obj = order;
mHandler.sendMessage(msg);
}
public void exit() {
if (mLooper != null) {
mLooper.quit();
mHandler = null;
mLooper = null;
}
}
}
private class Cooker extends Thread {
private String order;
public Cooker(String order) {
this.order = order;
start();
}
@Override
public void run() {
mMainHandler.sendEmptyMessage(COOKING_STARTED);
SystemClock.sleep(mRandomer.nextInt(50000));
mDeliverServer.deliver(order);
mMainHandler.sendEmptyMessage(COOKING_DONE);
}
}
private class DeliverServer extends HandlerThread {
private Handler mHandler;
public DeliverServer(String name) {
super(name);
start();
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper(), new Handler.Callback() {
public boolean handleMessage(Message msg) {
new Deliver((String) msg.obj);
return true;
}
});
}
public void deliver(String order) {
if (mHandler == null || getLooper() == null) {
return;
}
Message msg = Message.obtain();
msg.obj = order;
mHandler.sendMessage(msg);
}
public void exit() {
quit();
mHandler = null;
}
}
private class Deliver extends Thread {
private String order;
public Deliver(String order) {
this.order = order;
start();
}
@Override
public void run() {
mMainHandler.sendEmptyMessage(DELIVERING_STARTED);
SystemClock.sleep(mRandomer.nextInt(50000));
mMainHandler.sendEmptyMessage(ORDER_DONE);
}
}
}
相關(guān)文章
Flutter-AnimatedWidget組件源碼示例解析
這篇文章主要為大家介紹了Flutter-AnimatedWidget組件源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Android TextView跑馬燈效果實(shí)現(xiàn)方法
這篇文章主要介紹了Android TextView跑馬燈效果實(shí)現(xiàn)方法,涉及Android布局文件中相關(guān)屬性的設(shè)置技巧,非常簡單實(shí)用,需要的朋友可以參考下2016-01-01Android實(shí)現(xiàn)調(diào)用攝像頭拍照與視頻功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)調(diào)用攝像頭拍照與視頻功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04使用Android WebSocket實(shí)現(xiàn)即時(shí)通訊功能
即時(shí)通訊(Instant Messaging)最重要的毫無疑問就是即時(shí),不能有明顯的延遲,要實(shí)現(xiàn)IM的功能其實(shí)并不難,目前有很多第三方,比如極光的JMessage,都比較容易實(shí)現(xiàn)。本文通過實(shí)例代碼給大家分享Android WebSocket實(shí)現(xiàn)即時(shí)通訊功能,一起看看吧2019-10-10Android輕松實(shí)現(xiàn)多語言的方法示例
本篇文章主要介紹了Android輕松實(shí)現(xiàn)多語言的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11android初學(xué)者必須掌握的Activity狀態(tài)的四大知識點(diǎn)(必讀)
本篇文章主要介紹了android activity的四種狀態(tài),詳細(xì)的介紹了四種狀態(tài),包括Running狀態(tài)、Paused狀態(tài)、Stopped狀態(tài)、Killed狀態(tài),有興趣的可以了解一下。2016-11-11Kotlin使用協(xié)程實(shí)現(xiàn)高效并發(fā)程序流程詳解
這篇文章主要介紹了Kotlin使用協(xié)程實(shí)現(xiàn)高效并發(fā)程序流程,協(xié)程屬于Kotlin中非常有特色的一項(xiàng)技術(shù),因?yàn)榇蟛糠志幊陶Z言中是沒有協(xié)程這個(gè)概念的。那么什么是協(xié)程呢?它其實(shí)和線程有點(diǎn)相似,可以簡單地將它理解成一種輕量級的線程2023-01-01android12?SD如何動(dòng)態(tài)申請讀寫權(quán)限
這篇文章主要給大家介紹了關(guān)于android12?SD如何動(dòng)態(tài)申請讀寫權(quán)限的相關(guān)資料,從Android?6.0開始,權(quán)限不再是在manifest?件中粘貼?下即可,這時(shí)候權(quán)限也正式?進(jìn)?家的視野,需要的朋友可以參考下2023-07-07