一. Messenger是什么
二. Messenger的原理及使用
Server:首先需要在服务器创建Binder对象,如何创建呢?通过Messenger来创建,所以我们需要先构造Messenger对象,对于Messenger的构造方法有两种,如下:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
所以我们需要先构造一个Handler,这个Handler的作用其实就是处理消息。然后我们再通过这个Handler来构造Messenger对象,这个Messenger对象其实就是将客户端发送来的消息传递给Handler来处理,然后我们需要得到Binder对象,通过在Service的onBind方法中return Messenger.getBinder(),这样就得到了Binder对象。
Binder驱动:跟AIDL一样,还是Service。
Client:也是需要得到服务端的Binder对象在binder驱动层对应的mRemote引用,获得的方式是将ServiceConnection中的IBinder service当做参数传入Messenger的构造函数中,如:
Messenger mService = new Messenger(service);
然后就可以用mService.send(msg)给服务器发消息。实现跨进程通信。
因为这里是借助Messenger,所以无法调用服务器端的方法,只能通过message来传递消息。而当服务器需要回应客户端的时候,就需要客户端提供一个Messenger,然后服务器得到这个Messenger,因为在就像客户端向服务端发送请求的时候,也是服务器提供一个Messenger,然后客户端得到这个Messenger。那么如何实现呢?因为客户端和服务器已经建立了连接,所以只需要在客户端发送消息的时候,通过消息的replyTo参数向服务器传入一个Messenger,然后服务器在接收到客户端的消息的时候得到通过message的replyTo参数得到这个Messenger,然后利用这个向客户端发送消息就可以了。主要代码如下:
在客户端发送消息给服务器的时候:
message.replyTo=clientMessenger;
服务器接收消息的时候:
Messenger clientMessenger=msg.replyTo;
这样就在服务器端得到了客户端的Messenger,然后在服务器端通过clientMessenger.send(message);就向客户端发送了消息。
三. Bundle、Messenger和Message之间的联系
上面说到了Bundle、Messenger、Message这三个类,三个都实现了Parcelable接口,三个同时用于进程间通信,那么这三者有什么联系吗?
其实根据每一个类的构造方法以及主要函数,我们便可以知道这三者的联系了。现在我们把Messenger比喻为一个信使,信使的作用是派信;那么Message就比喻为信件、信封,即信使派的东西;那么Bundle是什么呢?Message里面保存了Bundle,那么bundle可以比喻为信纸,信纸上写满了各种我们要传递的信息。读到这里,读者应该明白了这三者在Messenger通讯方式内所扮演的角色了。
简单来说:Messenger把装有Bundle的Message发送到别的进程。
四. 重点
- 对于使用Messenger而言,底层其实是AIDL,但是没有AIDL灵活,因为这是借助Messenger来发送消息从而进行消息的传递,不能直接调用服务端的方法,而使用AIDL是直接可以调用服务端的方法。
- 对于服务端的Messenger的作用是将客户端传递的消息传递给Handler来处理,而客户端的是发送消息给服务端。
- Messenger是以串行的方式处理客户端发来的消息,当消息多的时候就就不合适了。而AIDL是可以并发处理客户端传来的消息。
1.创建一个Service模拟Server进程
一般的进程间通信多是在两个App之间,但一个App中也可以有多进程,这个很常见,如应用中的推送服务一般位于单独的进程。当然我们可以把这个Service创建到另一个App中,但为了方便测试,这里只是将该Service注册为另一个进程,但还是在同一个应用中。该Service的实现很简单,如下:
public class MessengerService extends Service {
private static final String TAG = MessengerService.class.getSimpleName();
public static final int MSG_CLIENT_TO_SERVER = 35;
public static final int MSG_SERVER_TO_CLIENT = 36;
private Messenger mMessenger;
private WorkThread mWorkThread;
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
mWorkThread = new WorkThread();
mWorkThread.start();
Log.i(TAG,"Server start -------");
}
@Override
public void onDestroy() {
super.onDestroy();
if (mWorkThread != null)mWorkThread.quit();
}
private class WorkThread extends Thread {
MessengerHandler mHandler;
@Override
public void run() {
Looper.prepare();
mHandler = new MessengerHandler();
prepareMessenger();
Looper.loop();
}
public void quit() {
mHandler.getLooper().quit();
}
private void prepareMessenger() {
mMessenger = new Messenger(mWorkThread.mHandler);
}
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_CLIENT_TO_SERVER:
Log.i(TAG,"Obtain Client Message-------" + msg.getData().get("msg"));
Messenger clientMessenger = msg.replyTo;
if (clientMessenger != null) {
try {
Message message=Message.obtain(null, MSG_SERVER_TO_CLIENT);
Bundle bundle=new Bundle();
bundle.putString("reply","I am Server,We received the message");
message.setData(bundle);
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
}
上述代码虽然简单,但有几点需要注意:
- 为什么Service中要开一个工作线程?因为Service作为四大组件之一,它是运行在主线程的,所以不能执行耗时操作,一旦进程间交互是耗时操作,那么Service所在进程就会阻塞,而Client端进程则不会阻塞。
- 该Service中创建了一个Messenger对象,并在onBind中返回了IBinder对象,这里是进程间通信的关键,在后面会详细分析。
- 该Service的子线程中创建了一个Handler,并关联给Messenger,用于进程间通信的消息处理。Handler消息处理跟我们平时用的一样,但有一点提一下,子线程是没有默认Looper的,因此需要自己创建并启动,否则子线程的Handler无法收到Message。
- Server端收到消息后,Toast一下“hello server”并显示Cient传过来的两个整数值。如果Client端也将自己的Messenger传过来了,则向Client端回复消息,将两个整数之和返回。
另外该Service在AndroidManifest.xml中的注册如下:
<service android:name=".MessengerService" android:enabled="true" android:exported="true" android:process=":remote"> </service>
android:process=":remote"
,将该Service置于另一个进程之中,从而可以在同一个App中模拟进程间通信。2. 创建一个Activity模拟Client进程
该Activity默认就是该App所在进程了,具体实现如下:
public class MessengerActivity extends AppCompatActivity
implements ServiceConnection, View.OnClickListener {
private static final String TAG = MessengerActivity.class.getSimpleName();
private boolean mBound = false;
private Messenger mMessenger;
private Button mBtnStart;
private Button mBtnBind;
private Button mBtnSend;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
initView();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(this);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
mBound = false;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnStart:
startRemoteService();
mBtnStart.setEnabled(false);
break;
case R.id.btnBind:
bindRemoteService();
mBtnBind.setEnabled(false);
break;
case R.id.btnSend:
sendRemoteService();
break;
}
}
private void initView(){
mBtnStart = findViewById(R.id.btnStart);
mBtnBind = findViewById(R.id.btnBind);
mBtnSend = findViewById(R.id.btnSend);
mBtnStart.setOnClickListener(this);
mBtnBind.setOnClickListener(this);
mBtnSend.setOnClickListener(this);
}
public void startRemoteService() {
Intent intent = new Intent();
intent.setClass(this, MessengerService.class);
intent.setPackage(this.getPackageName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(intent);
}
public void bindRemoteService() {
Intent intent = new Intent();
intent.setClass(this, MessengerService.class);
intent.setPackage(this.getPackageName());
bindService(intent, this, BIND_AUTO_CREATE);
}
public void sendRemoteService() {
if (!mBound){
Toast.makeText(MessengerActivity.this, "Service not bind", Toast.LENGTH_SHORT).show();
}
try {
Message message = Message.obtain(null, MessengerService.MSG_CLIENT_TO_SERVER);
Bundle bundle=new Bundle();
bundle.putString("msg", "I am Client,Obtained server side.");
message.setData(bundle);
message.replyTo = new Messenger(new ClientHandler());
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private static class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MessengerService.MSG_SERVER_TO_CLIENT:
Log.i(TAG,"Obtain Server message-------" + msg.getData().get("reply"));
break;
default:
break;
}
}
}
}
代码逻辑也很简单,界面有3个按钮,操作如下:
- 先启动Server端的Service,暂且叫做启动远程Service
- 绑定远程Service
- Client向Servcie端发送消息,并接收返回的消息
需要注意的有如下几点:
- 绑定远程Service后,Client端才拿到了Server端的Messenger引用。
- Client端的Messenger需要关联自己的Handler,用来处理从Server端收到的消息。这里也需要注意,理论上如果Server端与Client端交互也是耗时的话,也需要开子线程,这个例子中由于只是显示下消息,直接放在UI线程了。
- 如果需要双向通信,Client端需要通过Message的replyTo参数将自己的Messenger发到Server端。
3. 示例效果演示
以上示例的进程间通信效果演示如下:
Server:
文章评论