前言
在Android中實現IPC(進程間通信)
的方式有很多,本文講解如何使用Messenger
進行實現。Messenger
可以翻譯爲信使,用於跨進程傳輸Message
對象。在Message
對象中,我們可以附帶其他數據,這就實現了數據的跨進程傳遞。簡而言之,Messenger
是一種輕量級的IPC
方案,在它的底層封裝了AIDL
。
支持的數據類型
Messenger
只能傳遞Message
對象。因此,Message支持的數據類型就是Messenger支持的數據類型。Message
的arg1、arg2、what
字段各自可以附加一個int
型數據,而obj
字段可以附加一個Object
對象,但是隻支持系統提供的Parcelable
對象。因此,傳遞數據的重任就落在了Bundle
身上。通過Bundle,我們可以傳遞以下類型的數據:
- 基本數據類型
String
和CharSequence
- 實現
Parcelable
接口的對象 - 實現
Serializable
接口的對象
基本流程
服務端進程
首先我們要創建一個Service
,用於提供服務。在Service
中,我們需要創建一個Handler
對象,用於接收客戶端進程發來的數據。其次,通過這個Handler
創建一個Messenger
對象。在Messenger
對象中封裝有Binder
。因此,我們在Service
的onBind
方法中將Messenger
對應的Binder
對象返回。
客戶端進程
首先,我們需要綁定服務器端進程的Service
,並通過綁定後返回的IBinder
對象創建一個Messenger
。藉助這個Messenger
對象,我們就可以向服務器端進程發送Message
數據了。
雙向通信
通過上述步驟,我們只能做到從客戶端進程向服務端進程傳遞數據,即單向通信
。如果同時還要求從服務器端進程向客戶端進程傳遞數據,就需要在客戶端進程也創建一個Handler
對象,用於接收服務器端傳來的數據。此外,我們要通過這個Handler
創建一個Messenger
對象,並通過Message
的replyTo
字段傳遞給服務器端進程。服務器端可以從傳來的Message
中取出這個Messenger
對象,然後就可以使用它向客戶端傳遞數據了。這樣,就實現了客戶端和服務器端間的雙向通信
。
實現單向通信
服務器端:
public class ServerService extends Service {
private static final String TAG="ServerService";
public static final int DATA_FROM_CLIENT=1;
//自定義Handler,用於接收客戶端傳來的數據
private static class ServerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DATA_FROM_CLIENT:
Bundle bundle=msg.getData();
Log.v(TAG,bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER));
break;
default:
break;
}
}
}
private Messenger messenger=new Messenger(new ServerHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();//返回Messenger對象底層封裝的Binder
}
}
可以看到,我們在Service
中定義了一個靜態內部類ServerHandler
。同時,我們通過以下代碼創建了一個Messenger
對象:
private Messenger messenger=new Messenger(new ServerHandler());
在onBind
方法中,我們通過getBinder
方法返回了Messenger
封裝的Binder
對象。
return messenger.getBinder();//返回Messenger對象底層封裝的Binder
這樣,客戶端傳遞的數據就可以在ServerHandler
中處理了。在ServerHandler
中,我們通過以下代碼取出Message
中的數據:
Bundle bundle=msg.getData();
bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER);
同時,我們爲這個Service
指定一個私有進程名,這樣就讓這個Service
運行在獨立的進程中了。
<service
android:name=".ServerService"
android:process=":remote">
</service>
客戶端:
public class ClientActivity extends AppCompatActivity {
public static final String KEY_HELLO_TO_SERVER="HELLO_TO_SERVER";
private Messenger serverMessenger;//來自服務器端的Messenger
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serverMessenger=new Messenger(service);
Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服務器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
try {
serverMessenger.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_client);
Intent intent=new Intent(this,ServerService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);//解綁
}
}
可以看到,我們通過匿名內部類的方式創建了一個ServiceConnection
對象。在它的onServiceConnected
方法中,我們通過獲取到的IBinder
對象創建了一個Messenger
,通過這個Messenger
就可以向服務器端發送Message
了。
serverMessenger=new Messenger(service);
具體的發送代碼如下:
Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服務器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
serverMessenger.send(message);
我們先創建了一個Bundle
對象,並向其中存儲了一個字符串。隨後,我們創建了一個Message
對象,並將前面的Bundle
對象設置給Message
。最後,通過ServerMessenger
將Message
發送出去。
需要注意的是,一定要記得在Activity
的onDestroy
方法中解除對Service
的綁定。
unbindService(serviceConnection);//解綁
運行結果:
....../com.example.ipcdemo:remote V/ServerService: 你好,服務器~
實現雙向通信
我們在上面實現了一個單向通信的例子。但是要做到從服務器端給客戶端發送數據,還需要進行修改。主要改動在於客戶端,我們需要提供一個Handler
和Messenger
。具體改動如下,在主要的新增代碼前都添加了註釋//Add
:
服務器端:
public class ServerService extends Service {
private static final String TAG="ServerService";
public static final int DATA_FROM_CLIENT=1;
public static final String KEY_HELLO_TO_CLIENT="HELLO_TO_CLIENT";
//自定義Handler,用於接收客戶端傳來的數據
private static class ServerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DATA_FROM_CLIENT:
Bundle bundle=msg.getData();
Log.v(TAG,bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER));
//Add
Messenger clientMessenger=msg.replyTo;
Bundle bundleToClient=new Bundle();
bundleToClient.putString(KEY_HELLO_TO_CLIENT,
"你好,客戶端。我收到你的消息了。");
Message message=new Message();
message.what=ClientActivity.DATA_FROM_SERVER;
message.setData(bundleToClient);
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
private Messenger messenger=new Messenger(new ServerHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();//返回Messenger對象底層封裝的Binder
}
}
可以看到,在服務器端的主要改變是在Handler
中從Message
的replyTo
參數中獲取了客戶端提供的Messenger
對象。通過這個對象我們就可以向客戶端發送Message
了。
Messenger clientMessenger=msg.replyTo;
客戶端:
public class ClientActivity extends AppCompatActivity {
private static final String TAG="ClientActivity";
public static final String KEY_HELLO_TO_SERVER="HELLO_TO_SERVER";
public static final int DATA_FROM_SERVER=0;
private Messenger serverMessenger;//來自服務器端的Messenger
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serverMessenger=new Messenger(service);
Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服務器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
//Add
message.replyTo=messenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//Add
private static class ClientHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DATA_FROM_SERVER:
Bundle bundle=msg.getData();
Log.v(TAG,bundle.getString(ServerService.KEY_HELLO_TO_CLIENT));
break;
default:
break;
}
}
}
//Add
private Messenger messenger=new Messenger(new ClientHandler());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
Intent intent=new Intent(this,ServerService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);//解綁
}
}
可以看到,我們定義了一個靜態內部類作爲客戶端的Handler
類,並通過Handler
創建了一個Messenger
對象。這一過程和服務器端的做法一致。在綁定Service
時,我們將這個Messenger
作爲Message
的replyTo
參數傳遞給了服務器端。這樣,服務器端就可以獲取到客戶端提供的Messenger
了。具體代碼如下:
message.replyTo=messenger;
運行結果:
....../com.example.ipcdemo:remote V/ServerService: 你好,服務器~
....../com.example.ipcdemo V/ClientActivity: 你好,客戶端。我收到你的消息了。
小結
通過以上例子,可以看出Messenger
的使用是很方便的,畢竟系統已經爲我們做了封裝。但是,這種方式的侷限性也很明顯。首先,通過這種方式只能傳遞Message
,無法實現遠程方法調用(RPC)
。其次,從上文的代碼也可以看出Messenger
的消息傳遞是通過Handler
進行處理的,這意味着Messenger
的消息是串行處理的,顯然這不適合高併發的場景。如果有更高的交互要求,可以考慮使用AIDL
實現IPC
。具體請參考另一篇博客: