最近在學習Android開發藝術探索這本書,看到Messenger的跨進程通信,覺得書上講得不錯,所以在這里做個總結。
Messenger的思路很簡單,通過它可在不同進程傳遞Message對象,我們在Message中放入我們需要傳遞的數據,就可以輕松實現數據的進程間傳遞了。Messenger的底層是AIDL,它對AIDL做了封裝,使我們可以更簡單地進行進程間通信。
1.服務端進程
public class MessengerService extends Service {
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what==MSG){
Toast.makeText(MessengerService.this, msg.getData().getString("msg"), Toast.LENGTH_SHORT).show();
}
}
};
private Messenger messenger = new Messenger(handler);
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return messenger.getBinder();
}
}
看看這個服務端代碼,我們使用了一個Handler處理客戶端發送的Message,從Message取出我們所需的數據。
我們新建了一個Messenger對象,從構造參數我們可以看出它和handler相關聯。我們在onBind()方法中返回了messenger中的binder對象。
由于我們處理的是跨進程通信,所以別忘了在Manifest文件中給Service加上process屬性.
<service
android:name=".MessengerService"
android:process=":remote"/>
我們知道,一個應用的進程默認名稱為包名,應用內的所有組件都運行在這個進程內,如果想讓某個組件獨立運行在另一個進程,那么則加上process屬性,這里的“:remote”全程應該是包名:remote,省去了前面的包名。
2.客戶端進程##
public class MainActivity extends AppCompatActivity {
private Messenger messenger;
public static final int MSG = 1;
private Button btn;
private ServiceConnection connection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
Message message = Message.obtain(null,MSG);
Bundle bundle = new Bundle();
bundle.putString("msg","helloworld");
message.setData(bundle);
try {
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);
btn = (Button) findViewById(R.id.bindService);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,MessengerService.class); bindService(intent,connection,BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
這個代碼我們也比較熟悉了,跟平常bindService在客戶端和服務端在同一進程的寫法相似。區別在于使用了Messenger.
同樣地我們在客戶端也要新建一個Messenger對象,在構造參數里傳入返回的binder對象。
我們在Message中放入我們要的數據,借助的是Bundle,最后用messenger.send(message);發送過去即可。
我們運行一下程序,可見服務端收到了Messenger發來的信息。
在Messenger傳輸數據需要將數據放入Message.我們都知道,跨進程運輸的數據需要實現序列化,所以Messenger和Message必定實現了Parcelable接口,看下源碼
public final class Message implements Parcelable
public final class Messenger implements Parcelable
Message的載體有what,arg1,arg2,Bundle,object和replyTo,在同一進程內,obj很常用,但是在跨進程中就不行了,只有系統提供的Parcelable對象才可以通過它進行傳輸,我們自己定義的Parcelable對象是無法通過它傳輸的。但是我們還有Bundle,只要對象實現了Parcelable接口,利用bundle.putExtra(key,object)就能傳遞數據了,這里我們只演示傳遞String類型數據。
接下來我們再來看看服務端如何回復客戶端發送的消息。
當服務端接收到客戶端的消息時,我們也新建一個Messenger對象回復客戶端,代碼如下
//重點在這句話
Messenger messenger = msg.replyTo;
Message replyMessage = Message.obtain(null,REPLYMESSAGE);
Bundle bundle = new Bundle();
bundle.putString("reply","好的我知道了");
replyMessage.setData(bundle);
messenger對象由replyTo賦值。而replyTo來自客戶端的Messenger,這樣兩者就建立聯系。
再看看客戶端的修改,我們新建了Handler對象和Mssenger對象。
private Handler getReplyHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==REPLYMESSAGE){
Toast.makeText(MainActivity.this,msg.getData().getString("reply"),Toast.LENGTH_SHORT).show();
}
}
};
private Messenger getReplyMessenger = new Messenger(getReplyHandler);
然后我們需要把接收服務端回復的Messenger通過Message的replyTo參數傳遞給服務端,這句話有點難理解,多看幾遍。
private ServiceConnection connection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
Message message = Message.obtain(null,MSG);
Bundle bundle = new Bundle();
bundle.putString("msg","成功進行跨進程通信!");
message.setData(bundle);
//這句話與服務端的Messenger messenger = msg.replyTo;相對應,缺一不可
message.replyTo = getReplyMessenger;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
再運行一下程序
最后再放上一張Messenger的工作原理,找不到書上的原圖,只能自己畫了。