Messenger是什么?
Messenger是基于AIDL实现的轻量级IPC方案。
这里立马就会有疑问为什么要它呢?明明有了aidl
上节课大家学完了aidl进行binder通信是否感觉到使用起来其实还是有点复杂,毕竟通信什么的要写aidl,而且客户端和服务端都需要aidl文件,两个过程里面都需要,相对来说还是比较麻烦,对于项目过程中可能就是一些简单的跨进程数据传递,就是调用几个非常非常简单的方法,很多觉得都要写aidl成本比较大,那么有没有更加简单的方案来解决这个问题呢?
那就是本节课要讲解的Messenger。
那么接下来直接讲他的使用过程:
服务端需要实现一个Handler用于处理客户端发来的跨进程通信信息:
package com.example.servicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MessengerService extends Service {
Handler messengerHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
Log.i("test","MessengerService Messenger handleMessage msg = " + msg + " bundle key value = " + msg.getData().getString("bundleKey"));
Messenger clientSend = msg.replyTo;
Message toClientMsg = Message.obtain();
toClientMsg.what = 2;
// toClientMsg.obj = "I am replay from Server";
try {
clientSend.send(toClientMsg);
}catch (Exception e) {
Log.i("test","MessengerService clientSend error ",e);
}
break;
}
super.handleMessage(msg);
}
};
Messenger messenger = new Messenger(messengerHandler);
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
其次服务端构造出对应的Messenger:
服务端构造:
Messenger messenger = new Messenger(messengerHandler);
注意这里参数是messengerHandler即一个Handler
最后,当服务端的onBinder回调时候要返回Messenger的IBinder对象给客户端
客户端还是和以前一样通过bindService在ServiceConnection类的onServiceConnected获取到服务端的返回的IBinder,从而获取到服务端的Messenger代理类,调用send函数发送Message。所以Messenger能发送的信息只有Message能携带的信息。
public class MainActivity extends AppCompatActivity {
Handler messengerHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == 2) {
Log.i("test","messengerHandler handleMessage msg.what == 2 msg = " + msg);
}
super.handleMessage(msg);
}
};
Messenger messengerClientSend = new Messenger(messengerHandler);
Messenger messengerServer = null;
FloatingActionButton fab1 = findViewById(R.id.fab1);
fab1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,MessengerService.class);
Log.i("test","MessengerService onClick ");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
Log.i("test","MessengerService onServiceDisconnected name = " +name);
messengerServer = new Messenger(service);
sendMessageToServer();
} catch (Exception e) {
e.printStackTrace();
Log.i("test","error " ,e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test","client onServiceDisconnected name = " +name);
}
}, BIND_AUTO_CREATE);
}
});
void sendMessageToServer() throws RemoteException {
Message toServer = Message.obtain();
toServer.replyTo = messengerClientSend;
toServer.what = 1;
//toServer.obj = "hello I send from client"; 注意不可以 传递非parcel的对象,这个只能给obj赋值为parcel类型对象否则报错
Bundle bundle = new Bundle();
bundle.putString("bundleKey","bundleValue Client");
toServer.setData(bundle);
messengerServer.send(toServer);
}
}
大家这里注意客户端获取了服务端IBinder对象后,用它来构造客户端的Messenger,
messengerServer = new Messenger(service);
有了服务端Messenger后,那么就可以通过它与服务端进行通信了,通信的内容载体是我们最为属性Message,对他就是和Handler搭配的Message,它就是具体消息体,即你需要发送什么消息,都是把内容转换成Message对象既可以,这里我们案例中传递一个Bundle的对象,这个Bundle对象可以利用键值对方式装载各种各样类型数据。和Intent传递数据Bundle是一样的。注意这里message对象还有一个属性是replyTo ,这个是Messenger类型的,字面意思就是说这个消息发送过去,如果对方需要回复,就可以通过消息中的replyTo 的Messenger对象来进行回复,这里是不是也和我们上节课讲的binder双向通信一样,所以说Messenger这种方式本身就相当于自带了双向通信。
Messenger其实本质上也是使用aidl进行实现了,只是这个aidl是在Framework层面进行写好了,不需要你写,你也就没有在意,没有看到。这里对他的源码进行分析一下:
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
public IBinder getBinder() {
return mTarget.asBinder();
}
可以发现Messenger有两种构造函数,一个参数是Handler ,另一个是Ibinder。
从Messenger的构造方法(IMessenger.Stub.asInterface())可以看出它底层应该是使用的AIDL搞的.getBinder()其实是将调用了mTarget.asBinder(),而mTarget是我们传进来的Handler里面拿出来的,跟进Handler.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);
}
}
原来IMessenger是Handler的内部类MessengerImpl,它只有一个send方法.结合上面Messenger的源码,我们发现调用Messenger的send方法其实就是调用这里的MessengerImpl的send方法,然后这个send里面将Message转发给Handler#sendMessage(),最后也就是去了Handler#handleMessage()里面接收到这个Message.
MessengerImpl是继承自IMessenger.Stub,这一看就感觉是AIDL文件自动生成的嘛,easy.大胆猜测一下对应的aidl文件应该是IMessenger.aidl,我们去源码里面找IMessenger.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);
}
其实就是和我们使用AIDL一样将IXXX.Stub的父类IBinder通过onBind返回回去,客户端绑定的时候好拿到binder对象.接收客户端的消息时,是通过MessengerImpl转发给Handler来完成的,服务端这边定义的那个Handler就可以在handleMessage()中处理跨进程传递过来的Message,从而理解客户端想要调用什么服务,然后执行相应的逻辑.
再看客户端这边,在onServiceConnected()时,将服务端返回的IBinder对象放进Messenger里.
public void onServiceConnected(ComponentName name, IBinder service) {
try {
...
messengerServer = new Messenger(service);
sendMessageToServer();
} catch (Exception e) {
e.printStackTrace();
Log.i("test","error " ,e);
}
//Messenger.java
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
public Messenger(IBinder target) {
//这里asInterface 出来的其实就是 IMessenger.Stub.Proxy对象
mTarget = IMessenger.Stub.asInterface(target);
IBinder对象放进Messenger原来就是熟悉的操作IMessenger.Stub.asInterface(),简单.然后客户端这边给服务端发消息的时候通过构建出来的Messenger调用send方法发送,而Messenger内部send的实现其实就是调用IMessenger.Stub.Proxy(跨进程了)的send方法.调用之后,服务端那边在Handler的handleMessage里收到这条消息(Message),从而实现了跨进程通信.
客户端与服务端的通信与我们用AIDL的方式实现几乎一致,完全可以我们自己实现,Messenger只是帮我们封装好了而已.下面来看一下服务端与客户端的通信.
服务端需要与客户端通信的话,需要客户端在send消息的时候将客户端Messenger存放在消息的replyTo中.
Messenger messengerClientSend = new Messenger(messengerHandler);
...
void sendMessageToServer() throws RemoteException {
Message toServer = Message.obtain();
toServer.replyTo = messengerClientSend;
toServer.what = 1;
//toServer.obj = "hello I send from client"; 注意不可以 传递非parcel的对象,这个只能给obj赋值为parcel类型对象否则报错
Bundle bundle = new Bundle();
bundle.putString("bundleKey","bundleValue Client");
toServer.setData(bundle);
messengerServer.send(toServer);
}
将消息发送到服务端时,因为是跨进程,所以肯定需要用到序列化与反序列化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();
workSourceUid = source.readInt();
}
主要是看一下replyTo是怎么反序列化的,它调用了Messenger的readMessengerOrNullFromParcel方法:
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);//这里用asBinder()方法转成IBinder
}
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null; //这里重新用IBinder构造了Messenger
}
writeMessengerOrNullToParcel中将客户端的messenger.mTarget.asBinder()进行了写入,然后在readMessengerOrNullFromParcel时进行了恢复,而messenger.mTarget就是上面分析的MessengerImpl,asBinder()是其父类IMessenger.Stub里面的一个方法:
@Override
public android.os.IBinder asBinder() {
return this;
}
就是将自身返回出去.也就是说,服务端反序列化出来的replyTo对应Messenger中的IBinder其实就是客户端的MessengerImpl对象.于是服务端拿到这个Messenger就可以发送消息,通过这个IBinder对象跨进程通信,客户端就接收到消息了.