Android Messenger(跨进程)

1.Messenger
Service通过IBinder可以实现同一应用内同一进程间的通信,而不同进程间的通信最简单的方式是使用Messenger提供的通信接口。利用Messenger无需使用AIDL便可执行进程间通信IPC。

Messenger是一种基于消息Message的进程间通信的方式。可以实现同一应用不同进程间的通信,或者不同应用间的通信。

Messenger的使用步骤:
①服务端实现一个Handler,用来接收来自客户端每个调用的回调;
②Handler用于创建Messenger对象(对Handler的引用);
③Messenger创建一个IBinder,服务通过onBind()把它返回客户端;
④客户端使用IBinder将Messenger(引用服务的 Handler)实例化,然后使用Messenger将 Message对象发送给服务;
⑤服务在其Handler中(在 handleMessage() 方法中)接收并处理每个Message;

2.Messenger使用
服务端进程实现:
public class MessengerService extends Service{
static final int MSG_SAY_HELLO = 1;

//用于接收从客户端传递过来的数据
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg){
switch (msg.what) {
case MSG_SAY_HELLO:
Log.i(TAG, “thanks,Service had receiver message from client!”);
Thread.sleep(2000);//模拟耗时任务
break;
default:
super.handleMessage(msg);
}
}
}

//通过Handler创建Messenger
final Messenger mMessenger = new Messenger(new IncomingHandler());

//在onBind方法中,通过Messenger返回一个实现IBinder接口的实例对象
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, “Service is invoke onBind”);
return mMessenger.getBinder();
}
}
首先需要创建一个服务类MessengerService继承自Service,同时自定义一个Handler(IncomingHandler)对象来接收客户端进程发送过来的消息,并通过它的handleMessage()方法进行消息处理。
接着通过IncomingHandler对象创建一个Messenger对象,该对象是与客户端交互的特殊对象,然后在Service的onBind中返回这个Messenger对象的底层Binder即可。

客户端进程实现:
public class ActivityMessenger extends Activity{
//与服务端交互的Messenger
Messenger mService = null;
boolean mBound;//标志位,是否已绑定服务
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected ( ComponentName className, IBinder service){
//通过服务端传递的IBinder对象创建相应的Messenger,通过该Messenger对象就可以与服务端进行交互
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected ( ComponentName className) {
mService = null;
mBound = false;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_msger);
//绑定服务
bindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG,“onClick–>bindService”);
bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
}
});
//发送消息给服务端
sendMsgBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sayHello(v);
}
});
//解绑
unbindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mBound) {
Log.d(TAG,“onClick–> unbindService”);
unbindService(mConnection);
mBound = false;
}
}
});
}
public void sayHello(View v) {
if (!mBound) return;
// 创建与服务交互的消息Message
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
在客户端进程中创建一个ServiceConnection对象,该对象代表与服务端的链接,当调用bindService()将当前Activity绑定到MessengerService时,onServiceConnected方法被调用,利用服务端传递过来的底层Binder对象构造出与服务端交互的Messenger对象,接着创建与服务交互的消息实体Message,将要发送的信息封装在Message中,并通过Messenger实例对象发送给服务端。
最后,不要忘记在清单文件声明Service。

由于要测试不同进程的交互,则需要将Service放在单独的进程中,因此Service声明如下:
< service
android:name=".MessengerService"
android:process=":remote"
/>
其中android:process=":remote"代表该Service在单独的进程中创建。

运行程序,多次点击绑定服务,然后发送信息给服务端,最后解除绑定:
在这里插入图片描述
Service服务端收到了客户端发送的信息。
注意:在Messenger中进行数据传递必须将数据封装到Message中,因为Message和Messenger都实现了Parcelable接口,可以轻松跨进程传递数据,而Message可以传递的信息载体有what、arg1、arg2、Bundle以及replyTo。至于object字段,对于同一进程中的数据传递确实很实用,但对于进程间的通信,则显得相当尴尬,在android2.2前,object不支持跨进程传输,即便是android2.2之后也只能传递android系统提供的实现了Parcelable接口的对象,而开发者通过自定义实现Parcelable接口的对象无法通过object字段来传递,因此object字段的实用性在跨进程中就变得相当低了。不过还有Bundle对象,Bundle可以支持大量的数据类型。
从Log也可以看出无论是使用拓展Binder类的实现方式还是使用Messenger的实现方式,Service的生命周期方法的调用顺序是一样的,即onCreate、onBind、onUnBind、onDestroy,而且多次绑定中也只有第一次时才调用onBind()。

以上的例子演示了如何在服务端接收客户端发送的消息,但有时候可能还需要服务端能回应客户端,这时便需要提供双向消息传递了,下面就来实现一个服务端与客户端双向消息传递的简单例子。
在服务端,只需修改IncomingHandler,收到消息后,给客户端回复一条信息。
//用于接收从客户端传递过来的数据
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Log.i(TAG, “thanks,Service had receiver message from client!”);
//回复客户端信息,该对象由客户端传递过来
Messenger client=msg.replyTo;
//获取回复信息的消息实体
Message replyMsg = Message.obtain( null,MessengerService.MS G_SAY_HELLO);
Bundle bundle=new Bundle();
bundle.putString(“reply”,"ok~,I had receiver message from you! ");
replyMsg.setData(bundle);
//向客户端发送消息
try {
client.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
接着修改客户端,为了接收服务端的回复,客户端也需要一个接收消息的Messenger和Handler:
//用于接收服务器返回的信息
private Messenger mRecevierReplyMsg= new Messenger(new ReceiverReplyMsgHandler());

private static class ReceiverReplyMsgHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//接收服务端回复
case MessengerService.MSG_SAY_HE LLO:
Log.i(TAG, “receiver message from service:”+msg.getData().getString(“reply”));
break;
default:
super.handleMessage(msg);
}
}
}
除了添加以上代码,还需要在发送信息时把接收服务器端回复的Messenger通过Message的replyTo参数传递给服务端,以便作为通信桥梁:
public void sayHello(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
//把接收服务器端回复的Messenger通过Message的replyTo参数传递给服务端
msg.replyTo=mRecevierReplyMsg;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
到此服务端与客户端双向消息传递的简单例子修改完成,运行一下代码,看看Log打印:
在这里插入图片描述
服务端和客户端确实各自收到了信息,到此采用Messenger进行跨进程通信的方式分析完了,最后提供一张通过Messenger方式进行进程间通信的原理图:
在这里插入图片描述

通过代码可以看到服务端往客户端传递数据是通过msg.replyTo这个对象的。那么服务端完全可以做到,使用一个List或Map去存储所有绑定的客户端的msg.replyTo对象,然后想给谁发消息都可以。甚至可以把A进程发来的消息,通过B进程的msg.replyTo发到B进程那里去。

3.Messenger源码分析
Messenger的内部实现,实际上也是依赖于aidl文件实现的。
public final class Messenger implements Parcelable {
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public void send(Message message) throws RemoteException{
mTarget.send(message);
}
public IBinder getBinder() {
return mTarget.asBinder();
}
public writeToParcel(Parcel out, int flags) {
out.writeStrongBinder( mTarget.asBinder());
}
public static final Parcelable.Creator< Messenger> CREATOR = new Parcelable.Creator< Messenger>() {
public Messenger createFromParcel( Parcel in) {
IBinder target = in.readStrongBinder();
return target != null ? new Messenger(target) : null;
}
public Messenger[] newArray(int size) {
return new Messenger[size];
}
};
public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() : null);
}
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}
public Messenger(IBinder target){
mTarget = IMessenger.Stub.asInterface( target);
}
}
①客户端向服务端通信
服务端的onBind是这么写的:
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
那么点进去:
public IBinder getBinder() {
return mTarget.asBinder();
}
返回的是mTarget.asBinder();mTarget是哪来的呢?
别忘了前面构造mMessenger对象的代码:new Messenger(new Handler());
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
原来是Handler返回的,继续跟进去:
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}

private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
mTarget是一个MessengerImpl对象,asBinder实际上是返回this,也就是MessengerImpl对象;
这是个内部类,可以看到继承自IMessenger.Stub,然后实现了一个send方法,该方法就是将接收到的消息通过 Handler.this.sendMessage(msg);发送到handleMessage方法。
看到这,有没有觉得extends IMessenger.Stub这种写法异常的熟悉么?
传统写aidl文件,aapt就会生成IXXX.Stub类,然后我们服务端继承IXXX.Stub实现接口中的方法。
没错,其实这里内部其实也是依赖一个aidl生成的类,这个aidl位于:frameworks/base/core/java/android/os/IMessenger.aidl:
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
看到这里就明白了,Messenger并没有什么神奇之处,实际上就是依赖该aidl文件生成的类,继承了IMessenger.Stub类,实现了send方法,send方法中参数会通过客户端传递过来,最终发送给handler进行处理。

客户端首先通过onServiceConnected拿到sevice(Ibinder)对象,这里没什么特殊的,平时的写法也是这样的,只不过平时会这么写:IMessenger.Stub.asInterface(service)拿到接口对象进行调用;而我们的代码中是mService = new Messenger(service);
跟进去,会发现:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface( target);
}
和平时的写法一模一样!

到这里就可以明白,客户端与服务端通信,实际上和平时的写法没有任何区别,通过编写aidl文件,服务端onBind利用Stub编写接口实现返回;客户端利用回调得到的IBinder对象,使用IMessenger.Stub.asInterface(target)拿到接口实例进行调用。

②服务端与客户端通信
上个例子中,客户端send方法发送的是一个Message,这个Message.replyTo指向的是一个mMessenger,我们在Activity中初始化的。
那么将消息发送到服务端,肯定是通过序列化与反序列化拿到Message对象,看下Message的反序列化的代码:
#Message
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
}
主要看replyTo,调用的是Messenger.readMessengerOrNullFromParcel:
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}

public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() : null);
}
通过上面的writeMessengerOrNullToParcel可以看到,它将客户端的messenger.mTarget.asBinder()对象进行了恢复,客户端的message.mTarget.asBinder()是什么?
客户端也是通过Handler创建的Messenger,于是asBinder返回的是:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}

private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}

public IBinder getBinder() {
return mTarget.asBinder();
}
那么asBinder,实际上就是MessengerImpl extends IMessenger.Stub中的asBinder了。
#IMessenger.Stub
@Override
public android.os.IBinder asBinder() {
return this;
}
那么其实返回的就是MessengerImpl对象自己。到这里可以看到message.mTarget.asBinder()其实返回的是客户端的MessengerImpl对象。
最终,发送给客户端的代码是这么写的:
msgfromClient.replyTo.send(msgToClient);
send源码如下:
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
这个mTarget实际上就是对客户端的MessengerImpl对象的封装,那么send(message)(屏蔽了transact/onTransact的细节),这个message最终肯定传到客户端的handler的handleMessage方法中。

总结下Mesenger:
①客户端与服务端通信,利用的aidl文件,没什么特殊的。
②服务端与客户端通信,主要是在传输的消息上做了处理,让Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象做的与客户端的通信。

我的理解:
服务端通过handler创建一个Messenger对象(实际上是创建了一个IMessenger.Stub),然后在onBind()方法中将它传递给客户端,客户端在onServiceConnected方法中接收到这个binder对象后,通过newMessenger(binder)方法(实际上是IMessenger.Stub.asInterface()方法)得到服务端的Messenger对象,然后需要通信的时候,客户端使用这个Messenger对象调用send()方法(send()是在IMessenger接口里定义的接口方法,实际上在IMessenger.Stub里已经将这个方法进行了重写,并实现了具体逻辑,具体逻辑就是利用Handler发送消息handler.sendMessage(),这时候的Handler使用的是Messenger对应的Handler,也就是服务端的Handler,根据Handler机制,服务端的Handler会将这个消息取出并进行处理),之后服务端就可以收到这个消息,并进行处理。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值