Activity啟動後點擊一個介面按鈕後會開啟一個服務(暫訂為padService),在padService中會啟動一個線程(暫訂為Thread-3)發起Socket串連。我們項目中使用mina作為socket通訊架構,用過mina的同志們應該熟悉,Thread-3隻是負責監聽,具體的訊息處理是另外的線程。在我們的IoHandler中處理訊息,現在的問題是,我需要在IoHander的sessionOpened方法中給Activity一個訊息去更新UI介面,這個就涉及到不同線程間的通訊了。
網上搜尋後,在android中線程間通訊使用Handler,Looper,Message這幾個對象(不熟悉這些概念的同志們請自己查下)。
這是網上的一個使用例子:
<span style="font-size: medium;">public class Activity2 extends Activity implements OnClickListener{
Button button = null;
TextView text = null;
MyHandler mHandler = null;
Thread thread ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1);
button = (Button)findViewById(R.id.btn);
button.setOnClickListener(this);
text = (TextView)findViewById(R.id.content);
}軟體開發
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn:
thread = new MyThread();
thread.start();
break;
}
}
private class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {//處理訊息
text.setText(msg.obj.toString());
}
}
private class MyThread extends Thread{
@Override
public void run() {
Looper curLooper = Looper.myLooper();
Looper mainLooper = Looper.getMainLooper();
String msg ;
if(curLooper==null){
mHandler = new MyHandler(mainLooper);
msg = "curLooper is null";
}else{
mHandler = new MyHandler(curLooper);
msg = "This is curLooper";
}
mHandler.removeMessages(0);
Message m = mHandler.obtainMessage(1, 1, 1, msg);
mHandler.sendMessage(m);
}
}
}</span>
這個沒有問題,基本上三個對象的使用也很清楚,myHandler雖然是由子線程new出來的,但主線程持有引用,在我們的項目中不能用,因為我們幾個線程屬於不同的類,我嘗試用下面的方法解決:
在IoHandler中new一個android的handler,參數為主線程的Looper:
<span style="font-size: medium;">new Handler(Looper.getMainLooper()).sendMessage(msg);</span>
IoHandler所在的線程給主線程發送訊息(looper是主線程的,訊息也就放在主線程的訊息佇列裡了)
但是在主線程的handleMessage方法中得不到訊息,嘗試失敗。
那麼怎麼辦呢,讓IoHandler持有主線程的handler引用,具體做法有兩種方式:
1. 參數傳遞,把主線程的handler通過參數傳遞的形式傳到IoHandler中。
2. 靜態變數,把主線程的handler申明為公用靜態變數
<span style="font-size: medium;">public static Handler mainHandler;</span>
這兩種方式在主線程的 handleMessage的方法中都可以得到IoHandler發送的訊息。
本人使用的是靜態變數解決的,因為有好幾個來實現通訊,參數傳遞太麻煩。
那為什麼我的第一種嘗試是失敗的呢,我是把訊息放到主線程的訊息佇列了啊,這就要看android的一些實現機制了。
通過網路和android的api,本人的理解如下:
Looper是MessageQueue和Handler溝通的橋樑,Handler通過Looper把訊息放入訊息佇列(MessageQueue),你想把訊息發給誰,就把誰的looper作為參數傳給Handler
<span style="font-size: medium;">newHandler(Looper looper);</span>
Looper把訊息放入訊息佇列,並廣播訊息,這個不太好理解,我舉例如下:
主線程的Handler我們這樣定義:Handler mainHandler = new Handler(); 如果Handler沒有參數,預設為當前線程的Looper內陸運輸
子線程的Handler我們這樣定義: Handler subHandler = newHandler(Looper.getMainLooper()); 參數為主線程的Looper
這樣兩個線程都會把訊息放入主線程的訊息佇列裡了。
現在mainHandler.sendMessage(),
訊息進入主線程的訊息佇列,Looper廣播訊息,其實就是調用mainHandler的dispatchMessage方法,所有持有
mianHandler引用的類都可以收到訊息,注意啊,現在subHandler並不能接受到訊息,因為Looper並沒有調用subHandler的
dispatchMessage方法,所以應該這樣理解廣播,A發送訊息,那麼A的Looper就調用A的dispatchMessage方法,別的
B,C, D雖然也是A的Looper,但沒有A的引用,所以B,C,D是接受不到訊息的,如果B,
C,D持有A的引用,但B,C,D不用A的Looper,那麼也是接受不到訊息的。這點在開發時要特別注意。
以上是我在使用Looper, Handler ,Message中的一些問題,可能有理解錯的地方,請大大們指出來。
我的疑惑是難道子線程必須持有主線程的引用才可以給主線程發送訊息嗎?要知道我們的子線程並不一定和主線程一個類,可能在別的類中,這個引用傳遞實在太麻煩了,期望有更好的解決方式。