关于Android进程间的通信,在第一篇文章中我们通过传递Parcel对象,利用IBinder完成了进程间的通信,在第二篇文章 中我们通过在客户端创建一个aidl的文件,在服务端实例化由aapt生成的stub类的对象来完成了进程间的通信。今天我们来使用另一种更简洁的方法—Messenger来实现进程间的通信。
进程之间不能共享内存数据, 但是可以进行通信, 除了简单的Intent通信, 也可以使用Messenger, Messenger基于AIDL实现, 顺序执行, 不支持并发. 为了区分通信的始末, 我们暂定发送数据是客户端, 接收数据是服务端. 本文介绍Messenger的使用方式。
Messenger–信使,类的继承关系:
public final class Messenger extends Object implements Parcelable //实现了Parcelable接口
定义:
1 Reference to a Handler, //一个Messenger关联了一个Handler
2 which others can use to send messages to it.
3 This allows for the implementation of message-based communication across processes, //基于message的进程间通信
4 by creating a Messenger pointing to a Handler in one process,
5 and handing that Messenger to another process.
解释为:Messenger引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。
以前我们使用Handler+Message的方式进行通信,都是在同一个进程中,从线程持有一个主线程的Handler对象,并向主线程发送消息。
可以看到,我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果。
远程通过
serviceMessenger=new Messenger(new ServiceHandler());
创建一个信使对象
Intent intent =new Intent(MainActivity.this,MessengerService.class);
bindService(intent, mConn, Context.BIND_AUTO_CREATE);
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("TEST","MessengerService-->onBind()");
return serviceMessenger.getBinder();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service){
Log.i("TEST","onServiceConnected()");
mServiceMessenger = new Messenger(service);
mTvState.setText("连接成功!");
}
这里虽然是new了一个Messenger,但我们查看它的实现:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
发现它的mTarget是通过Aidl得到的,实际上就是远程创建的那个。
Message msgFromClient = Message.obtain(null, MSG_SUM);
mServiceMessenger .send(msgFromClient);
这样远程服务端的Handler对象就能收到消息了,然后可以在其handlerMessage(Message msg)方法中进行处理。(该Handler对象就是第一步服务端创建Messenger时使用的参数ServiceHandler).
经过这5个步骤貌似只有客户端向服务端发送消息,这样的消息传递是单向的,那么如何实现双向传递呢?
msgFromClient.replyTo = mClientMessenger;
将自己的信使设置到消息中,这样服务端接收到消息时同时也得到了客户端的信使对象mClientMessenger了,然后服务端可以通过/得到客户端的信使对象,并向它发送消息 mClientMessenger= msg.replyTo; mClientMessenger.send(message);
即完成了从服务端向客户端发送消息的功能,这样客服端可以在自己的Handler对象的handlerMessage方法中接收服务端发送来的message进行处理。
双向通信宣告完成。下面来DEMO验证上面的实现双方通信的六个步骤。
DEMO分为服务端、客户端,实现一个计算三个整数的和。
客户端由普通的MainActivity充当,触发点击事件时,客户端会向服务端发送数据(DEMO中是发送三个整数);服务端由一个MessengerService充当,用于接收客户端发送过来的数据,并求和,然后将求和的结果返回到客户端。最后客户端接收服务端返回的结果,并展示在界面上。
/** * Created by Administrator on 2016/6/23. */
public class MessengerService extends Service{
private static final int MSG_SUM = 0x110;
private Messenger serviceMessenger;
@Override
public void onCreate() {
super.onCreate();
Log.i("TEST","MessengerService-->onCreate()");
serviceMessenger=new Messenger(new ServiceHandler());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("TEST","MessengerService-->onBind()");
return serviceMessenger.getBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("TEST","MessengerService-->onUnbind()");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i("TEST","MessengerService-->onDestroy()");
super.onDestroy();
}
private static class ServiceHandler extends Handler{
@Override
public void handleMessage(Message msgfromClient) {
Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息
switch (msgfromClient.what){
//msg 客户端传来的消息
case MSG_SUM:
msgToClient.what = MSG_SUM;
try{
//模拟耗时
Thread.sleep(2000);
//msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;
//msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2+(int)(msgfromClient.obj);
int arg1=Integer.parseInt(msgfromClient.getData().getString(Constans.ARG1));
int arg2=Integer.parseInt(msgfromClient.getData().getString(Constans.ARG2));
int arg3=Integer.parseInt(msgfromClient.getData().getString(Constans.ARG3));
msgToClient.arg1=arg1;
msgToClient.arg2 = arg1 + arg2+arg3;
msgfromClient.replyTo.send(msgToClient);
} catch (InterruptedException e){
e.printStackTrace();
} catch (RemoteException e){
e.printStackTrace();
}
break;
}
super.handleMessage(msgfromClient);
}
}
}
<service android:name=".MessengerService" android:process=".remote"></service>
public class MainActivity extends AppCompatActivity {
private static final int MSG_SUM = 0x110;
//显示连接状态
private TextView mTvState;
private Button mBtnAdd;
private LinearLayout mLyContainer;
private Messenger mServiceMessenger;
private Messenger mClientMessenger;
private int mA;
private ServiceConnection mConn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service){
Log.i("TEST","onServiceConnected()");
mServiceMessenger = new Messenger(service);
mTvState.setText("连接成功!");
}
@Override
public void onServiceDisconnected(ComponentName name){
mServiceMessenger = null;
mTvState.setText("disconnected!");
Log.i("TEST","onServiceDisconnected()");
}
};
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mClientMessenger = new Messenger(new ClientHandler());
mTvState = (TextView) findViewById(R.id.id_tv_callback);
mTvState.setText("未连接");
mBtnAdd = (Button) findViewById(R.id.id_btn_add);
mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container);
mBtnAdd.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
if (mServiceMessenger==null){
Toast.makeText(MainActivity.this, "服务未连接或者被异常杀死!", Toast.LENGTH_SHORT).show();
return;
}
performCalculation();
}
});
findViewById(R.id.connect).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
connectService();
}
});
findViewById(R.id.disconnect).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
disconnectService();
}
});
}
private void performCalculation(){
try {
int a = mA++;
int b = (int) (Math.random() * 100);
int c = (int) (Math.random() * 100);
/**创建一个tv,添加到LinearLayout中*/
TextView tv = new TextView(MainActivity.this);
tv.setText(a + " + " + b + " + " + c + " = caculating ...");
tv.setTextSize(20);
tv.setId(a);
mLyContainer.addView(tv);
// Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);
// Message msgFromClient = Message.obtain(null, MSG_SUM, a, b,c);
// 这种方式传递参数c会报错,Can't marshal non-Parcelable objects across processes.
Message msgFromClient = Message.obtain(null, MSG_SUM);
Bundle data=new Bundle();//Bundle实现了Parcelable接口
data.putString(Constans.ARG1,String.valueOf(a));
data.putString(Constans.ARG2,String.valueOf(b));
data.putString(Constans.ARG3,String.valueOf(c));
msgFromClient.setData(data);
msgFromClient.replyTo = mClientMessenger;
/**往服务端发送消息*/
mServiceMessenger.send(msgFromClient);
} catch (RemoteException e){
e.printStackTrace();
}
}
@Override
protected void onDestroy(){
super.onDestroy();
unbindService(mConn);
}
public void connectService(){
Intent intent =new Intent(MainActivity.this,MessengerService.class);
bindService(intent, mConn, Context.BIND_AUTO_CREATE);
}
public void disconnectService(){
unbindService(mConn);
mServiceMessenger=null;
mTvState.setText("未连接");
}
private class ClientHandler extends Handler{
@Override
public void handleMessage(Message msgFromServer){
switch (msgFromServer.what){
case MSG_SUM:
TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1);
tv.setText(tv.getText() + "=>" + msgFromServer.arg2);
break;
}
super.handleMessage(msgFromServer);
}
}
}
06-23 17:00:24.490 9869-9869/.remote I/TEST: MessengerService-->onCreate()
06-23 17:00:24.490 9869-9869/.remote I/TEST: MessengerService-->onBind()
06-23 17:00:24.500 9701-9701/com.troy.messengersample I/TEST: onServiceConnected()
06-23 17:00:25.930 9869-9869/.remote I/TEST: MessengerService-->onUnbind()
06-23 17:00:25.930 9869-9869/.remote I/TEST: MessengerService-->onDestroy()
(1)掌握使用Messenger实现进程间的通信;
(2)掌握Messenger通信之间使用Bundle或者Message传输数据;
(3)理解Messenger与Handler之间的关系;
(4)理解Messenger与Binder之间的关系;
学习资料致谢:
1 Android 基于Message的进程间通信 Messenger完全解析
http://blog.csdn.net/lmj623565791/article/details/47017485
2 Android 进程使用 Messenger 通信
http://www.wangchenlong.org/2016/05/17/1605/171-android-messenger/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io