每個(gè)系統(tǒng)都會(huì)有相應(yīng)的 IPC 機(jī)制以方便開發(fā)者來進(jìn)行進(jìn)程間通訊,那么首先我們來梳理一下 Android 中的 IPC 機(jī)制有哪些:
- 組件間使用 Intent 傳遞數(shù)據(jù);
- 文件共享;
- Messenger;
- AIDL;
- ContentProvider;
- Socket;
這6種方式各有各的優(yōu)缺點(diǎn)和適用場(chǎng)景,在開發(fā)過程中最合適當(dāng)前需求的才是最好的。至于它們各自適用場(chǎng)景此處就不詳述了。今天主要來講一講這幾種機(jī)制中,功能強(qiáng)大但是使用起來卻非常簡單的 Messenger。
Messenger
Messenger 即信使的意思, 看起來它就與 Message 對(duì)象有關(guān),因?yàn)檫@個(gè)對(duì)象就是用來在不同進(jìn)程中傳遞 Message 對(duì)象的。
Messenger 是一種輕量級(jí)的 IPC 方案,它的底層實(shí)現(xiàn)是 AIDL。 因?yàn)樗鼘?duì) AIDL 做了封裝,所以使用起來非常簡單,就類似于我們綁定一個(gè) Service 一樣。同時(shí),由于它一次處理一個(gè)請(qǐng)求,所以在服務(wù)端我們不用考慮線程同步的問題,因?yàn)樵诜?wù)端中不存在并發(fā)執(zhí)行的情況。
下面我們就一步一步來實(shí)現(xiàn)一個(gè) Messenger 的例子,先從客戶端發(fā)送消息到服務(wù)端開始,然后再逐步完成其雙向通訊的功能。
Messenger 實(shí)現(xiàn)
要實(shí)現(xiàn)一個(gè) Messenger 的雙向通信的例子,那么肯定就需要實(shí)現(xiàn)服務(wù)端和客戶端這兩個(gè)部分。 我們先把實(shí)現(xiàn) Messenger 的幾個(gè)步驟列出來,再來代碼演示:
-
服務(wù)端進(jìn)程
- 創(chuàng)建一個(gè) Service 來處理客戶端的連接請(qǐng)求;
- 創(chuàng)建一個(gè) Handler 并通過它來創(chuàng)建一個(gè) Messenger 對(duì)象;
- 在 Service 的 onBind 函數(shù)中返回這個(gè) Messenger 對(duì)象底層的 Binder ;
-
客戶端進(jìn)程
- 綁定服務(wù)端的Service;
- 綁定成功后用服務(wù)端返回的 IBinder 對(duì)象創(chuàng)建一個(gè) Messenger;
- 通過這個(gè) Messenger 向服務(wù)端發(fā)送消息,消息類型為 Message 對(duì)象;
- 創(chuàng)建一個(gè) Handler 并創(chuàng)建一個(gè)新的 Messenger 對(duì)象;
- 將這個(gè)新的 Messenger 對(duì)象通過 Message 的 replyTo 參數(shù)傳遞給服務(wù)端,服務(wù)端就可以通過這個(gè) replyTo 參數(shù)回應(yīng)客戶端了;
客戶端發(fā)送信息到服務(wù)端
首先是服務(wù)端,我們需要實(shí)現(xiàn)一個(gè)典型的 Service 。在這個(gè) Service 中,創(chuàng)建一個(gè) MessengerHandler 對(duì)象用來接收客戶端的消息,并創(chuàng)建了一個(gè)與該Handler 相關(guān)聯(lián)的 Messenger,在 onBind 方法中返回它里面的 Binder 對(duì)象 :
public class MessengerService extends Service {
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 10086:
Log.i("swifter", "服務(wù)端收到消息:"+msg.getData().getString("msg"));
break;
}
}
}
//此Messenger將客戶端發(fā)送的消息傳遞給 MessengerHandler
private Messenger messenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
為了能夠讓該 Service 能夠在獨(dú)立的線程中,我們制定其 process :
<service android:name=".MessengerService"
android:process=":remote" />
然后看看客戶端的實(shí)現(xiàn),基本就是一個(gè) Activity 綁定 Service 的標(biāo)準(zhǔn)寫法:
public class MainActivity extends Activity {
private Messenger messenger;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
//創(chuàng)建完成Messenger后就可以通過Messenger 來發(fā)送 Message 了
Message message = Message.obtain();
message.what = 10086;
Bundle bundle = new Bundle();
bundle.putString("msg", "this is message from client.");
message.setData(bundle);
try { //通過信使發(fā)送信息
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//綁定服務(wù)
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
運(yùn)行能看到log輸出:
I/swifter: 服務(wù)端收到消息:this is message from client.
在 Messenger 中進(jìn)行數(shù)據(jù)傳遞必須將數(shù)據(jù)放入 Message 中,而 Messenger 和 Message 都實(shí)現(xiàn)了 Parcelable 接口,因此可以跨進(jìn)程傳輸。簡單來說, Message 中所支持的數(shù)據(jù)類型就是 Messenger 所支持的傳輸類型。但是作為規(guī)范,還是使用 Bundle 來進(jìn)行要傳輸?shù)臄?shù)據(jù)的封裝是最好的。
服務(wù)端回應(yīng)客戶端
上面的例子演示了如何在服務(wù)端接受到客戶端發(fā)送的消息,為了進(jìn)行雙向通訊,下面就演示服務(wù)端如何回應(yīng)客戶端。
首先是服務(wù)端,需要更改的地方不多,只是在收到消息之后回復(fù)一條消息:
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 10086:
Log.i("swifter", "服務(wù)端收到消息:"+msg.getData().getString("msg"));
Messenger replyMessenger = msg.replyTo;
Message replyMessage = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("reply", "服務(wù)端已經(jīng)收到消息");
replyMessage.what = 10010;
replyMessage.setData(bundle);
try {
replyMessenger.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
在客戶端上,為了接受服務(wù)端的信息,客戶端也需要實(shí)現(xiàn)一個(gè) Messenger :
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 10010:
Log.i("swifter", "客戶端收到消息:"+msg.getData().getString("reply"));
break;
}
}
}
private Messenger replyMessenger = new Messenger(new MessengerHandler());
除此之外,我們還需要將這個(gè)replyMessenger設(shè)置到要發(fā)送的 Message 的 replyTo 參數(shù)中去,這樣服務(wù)器才能拿到這個(gè)參數(shù)并回應(yīng)信息。
message.replyTo = replyMessenger;
運(yùn)行程序能看到log如下:
I/swifter: 服務(wù)端收到消息:this is message from client.
I/swifter: 客戶端收到消息:服務(wù)端已經(jīng)收到消息
至此,使用 Messenger 進(jìn)行客戶端與服務(wù)端之間的雙向通信還是蠻容易的,相比 AIDL, 這個(gè)方案相當(dāng)?shù)谋憬荨R驗(yàn)槭褂玫氖谴蟹绞教幚硇畔?,所?Messenger 比較適用于第并發(fā)的一對(duì)多的通信。不同的場(chǎng)景下還是需要找到最合適的方案。還是那句話,每個(gè)方案都是針對(duì)不同的業(yè)務(wù)場(chǎng)景而設(shè)計(jì)的,總之,找到合適的就是最好的。
