Messenger是通过Message和Handler的方式实现的进程间的通信。它的实现步骤大体上可以分为两个部分:
1、继承Service实现自己的服务,并通过Handler处理来自客户端的数据
2、客户端实现ServiceConnection接口,获取服务器实例,并发送数据
其实Messenger本质上是AIDL的高度封装,它的底层实现还是基于Binder的。为什么这么说呢,我们可以找一下Messenger的源码:
public final class Messenger implements Parcelable {
private final IMessenger mTarget;
/**
* Create a new Messenger pointing to the given Handler. Any Message
* objects sent through this Messenger will appear in the Handler as if
* {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
* been called directly.
*
* @param target The Handler that will receive sent messages.
*/
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
/**
* Send a Message to this Messenger's Handler.
*
* @param message The Message to send. Usually retrieved through
* {@link Message#obtain() Message.obtain()}.
*
* @throws RemoteException Throws DeadObjectException if the target
* Handler no longer exists.
*/
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
/**
* Retrieve the IBinder that this Messenger is using to communicate with
* its associated Handler.
*
* @return Returns the IBinder backing this Messenger.
*/
public IBinder getBinder() {
return mTarget.asBinder();
}
......
/**
* Create a Messenger from a raw IBinder, which had previously been
* retrieved with {@link #getBinder}.
*
* @param target The IBinder this Messenger should communicate with.
*/
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
}
我们摘取了部分Messenger的源码,可以看到它有两个构造函数,一个传递Handler另一个传递IBinder,我们先看一下Handler的那个构造方法,我们发现它调用了Handler里面的getIMessenger()这个函数,我们跟到Handler类里面发现这个函数返回的是一个IMessenger的实例,而这个实例的本质就是AIDL:
@UnsupportedAppUsage
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);
}
}
另一个传递IBinder的构造函数我们也发现了AIDL的影子。所以说Messenger是AIDL的高度封装。
接下来我们来基于Messenger实现夸进程的通信。
1、实现Service
首先我们定义一个YangService继承自Service,然后实现onBind方法,这个方法要求我们返回一个IBinder的实例。Messenger源码中有一个getBinder()的方法,可以为我们返回一个IBinder实例,我们通过构造一个Messenger对象,然后再调用getBinder()就可以搞定了。Service部分代码:
@Override
public IBinder onBind(Intent intent) {
Messenger messenger = new Messenger(mHandler);
return messenger.getBinder();
}
为了构建一个Messenger实例,我们需要一个Handler对象,因此需要在Service中创建一个YangHandler继承自Handler,然后重写handlerMessage()方法,在这个方法里,我会可以拿到客户端发送过来的消息,进行解析。
private static class YangHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//获取客户端传递的消息
Bundle clientBundle = msg.getData();
String clientMsg = clientBundle.getString("client");
Log.d(TAG,"客户端消息:"+clientMsg);
//收到消息之后,给客户端回复
Message serviceMsg = Message.obtain();
Bundle serviceBundle = new Bundle();
serviceBundle.putString("service","你好客户端,谢谢你的祝福,我会一直稳定运行下去!");
serviceMsg.setData(serviceBundle);
try {
//调用客户端的Messenger发送消息给客户端
msg.replyTo.send(serviceMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
如果想让服务器相应客户端的消息,可以在发送消息的时候附加一个replyTo对象,这个是一个Messenger对象,我们是通过调用Messenger的send()方法发送一个Message的。它的本质是调用了Handler的Handler的sendMessage()方法。
Messenger的send方法,可以看到它调用了Handler的send方法
/**
* Send a Message to this Messenger's Handler.
*
* @param message The Message to send. Usually retrieved through
* {@link Message#obtain() Message.obtain()}.
*
* @throws RemoteException Throws DeadObjectException if the target
* Handler no longer exists.
*/
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
进入Handler源码可以看到,最后调用的是Handler的sendMessage()方法
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
服务器代码写完之后,记得去AndroidMainfest.xml文件中声明service标签,这里我多写了一个action,表示这个服务既可以显式调用,也可以隐式调用。
<service
android:name=".YangService"
android:enabled="true"
android:exported="true"
android:process=":yzy">
<intent-filter>
<action android:name="YANGZHENYU.MSG.SERVIEC"/>
</intent-filter>
</service>
接下来我们开始写客户端的代码
2、实现客户端
客户端的实现相对来说比较简单,首先我们需要实现一个ServiceConnection的实例,然后在onServiceConnected()方法中获取服务器的Messenger的实例。
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
接下来我们需要定义一个Messenger,这一步和服务器端一样,也是通过调用Handler参数的构造方法创建的。因此我们还需要创建一个Handler,客户端的Handler的相关代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ClientHandler mHandler = new ClientHandler();
private Messenger mClient = new Messenger(mHandler);
...
private static class ClientHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String serviceMsg = bundle.getString("service");
Log.d(TAG,"服务器消息:"+serviceMsg);
}
}
}
接下来我们就可以通过Intent和服务器建立连接了,这里我才用的是显式意图,如果采用隐式意图,需要注意在Android5.0之后需要设置隐式意图的包名,否则会报错。
Intent intent = new Intent(this,YangService.class);
bindService(intent,mConn,BIND_AUTO_CREATE);
发送按钮要做的事情也比较简单,在之前的ServiceConnection的onServiceConnected()方法中我们已经拿到了服务器端的Messenger并保存了起来,当我们按下发送问候的时候,就是通过这个实例向服务器发送的消息。为了能够让服务器相应我们的问候,在消息中我们通过Message的replyTo变量将客户端的mClient(Messenger对象)传递给了服务器:
Message clientMsg = Message.obtain();
Bundle clientBundle = new Bundle();
clientBundle.putString("client","你好服务器,我是客户端,希望你运行稳定!");
clientMsg.setData(clientBundle);
clientMsg.replyTo = mClient;
try {
mService.send(clientMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
为了方便测试我这里有两个按钮,一个是绑定服务,另一个是发送问候。
接下来我们就简单的测试一下,首先点击绑定服务按钮和服务器建立连接,当我们绑定服务之后,会发现目前有两个进程,一个是主进程,另一个是yzy的子进程。(这个名字可以随意起)
然后我们点击发送问候,看一下控制台的输出内容:
//服务器打印的消息
com.yangzhenyu.ipcmessenger:yzy D/yang: 客户端消息:你好服务器,我是客户端,希望你运行稳定!
//客户端打印的消息
com.yangzhenyu.ipcmessenger D/yang: 服务器消息:你好客户端,谢谢你的祝福,我会一直稳定运行下去!
我们可以看到服务器收到了来自客户端的问候,然后紧接着客户端收到了来自服务器的回复信息。至此整个基于Messenger的进程通信流程执行完毕。