【朝花夕拾】跨进程通信,你只知道AIDL,就OUT了

时间:2021-04-30 02:52:01

前言

转载请声明,转自【https://www.cnblogs.com/andy-songwei/p/11774836.html】,谢谢!

提起跨进程通信,大多数人首先会想到AIDL。我们知道,用AIDL来实现跨进程通信,需要在客户端和服务端都添加上aidl文件,并在服务端的Service中实现aidl对应的接口。如果还需要服务端给客户端发送信息,还需要再添加回调相关的aidl文件,以及使用RemoteCallbackList来辅助实现该功能。在我的另外一篇文章【朝花夕拾】Android性能篇之(七)Android跨进程通信篇中,就专门介绍过AIDL来实现客户端和服务端互相通信的方式,不清楚的可以看看这篇文章的介绍。本文将介绍一下另外一种更简单的方式——Messenger,来实现客户端和服务端跨进程互相通信。

本文的主要内容如下:

【朝花夕拾】跨进程通信,你只知道AIDL,就OUT了

一、Messenger简介

Messenger翻译为信使,顾名思义,就是用于传递信息的,通过它可以在不同进程中传递Message对象。在Message中放入我们需要传递的信息,然后通过Messenger将Message传递给对方,就可以轻轻松松实现跨进程数据传递。实际上Messenger是一种轻量级的IPC(跨进程通信)方式,它的底层仍然是实现的AIDL。它是一种基于消息的进程通信,就像子线程和UI线程发送消息那样,Demo中服务端和客户端使用的Handler,正好说明了这一点。

二、Messenger使用代码示例

话不多说,咱们这里看看一个完整的Demo,来直观感受一下Messenger的使用。本Demo演示的功能很简单,客户端发送消息给服务端,服务端收到消息后再发送消息给客户端作为响应。

1、服务端代码实现

 public class MessengerService extends Service {
private static final String TAG = "Messenger-Demo";
private static final int MSG_CLIENT = 0x001;
private static final int MSG_SERVER = 0X002;
private static final String KEY_CLIENT = "key_client";
private static final String KEY_SERVER = "key_server"; private final Messenger mMessenger = new Messenger(new MessageHandler()); @Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
} private static class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CLIENT:
Log.d(TAG, "receive msg from Client:" + msg.getData().getString(KEY_CLIENT));
Messenger messenger = msg.replyTo;
Message serverMsg = Message.obtain();
serverMsg.what = MSG_SERVER;
Bundle bundle = new Bundle();
bundle.putString(KEY_SERVER, "Hello Client! I am fine, thank you");
serverMsg.setData(bundle);
try {
messenger.send(serverMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
}

对应清单文件中的注册

 <service
android:name=".MessengerService "
android:exported="true"/>

这里需要注意的是第三行,该Service需要提供给其它应用调用,需要将该属性值设置为true。

2、客户端代码实现

 public class MessengerClientActivity extends AppCompatActivity {

     private static final String TAG = "Messenger-Demo";
private static final int MSG_CLIENT = 0x001;
private static final int MSG_SERVER = 0X002;
private static final String KEY_CLIENT = "key_client";
private static final String KEY_SERVER = "key_server";
private static final String SERVER_PKGNAME = "com.example.messageserver";
private static final String SERVICE_PATH = "com.example.messageserver.MessengerService";
private Messenger mRemoteMessenger;
private Messenger mLocalMessenger = new Messenger(new MessengerClientHandler()); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG,"onCreate");
bindService();
} private void bindService() {
Intent intent = new Intent();
ComponentName componentName = new ComponentName(SERVER_PKGNAME, SERVICE_PATH);
intent.setComponent(componentName);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} private static class MessengerClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SERVER:
Log.d(TAG, "receive msg from Server:" + msg.getData().getString(KEY_SERVER));
break;
default:
break;
}
super.handleMessage(msg);
}
} private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteMessenger = new Messenger(service);
Message clientMsg = Message.obtain();
clientMsg.what = MSG_CLIENT;
Bundle bundle = new Bundle();
bundle.putString(KEY_CLIENT, "Hello,Server! How are you ?");
clientMsg.setData(bundle);
clientMsg.replyTo = mLocalMessenger;
try {
mRemoteMessenger.send(clientMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
} @Override
public void onServiceDisconnected(ComponentName name) { }
}; @Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
}

3、运行

运行时先启动服务端,再启动客户端,可以看到如下log信息:

1 15185-15185/com.example.messageserver D/Messenger-Demo: receive msg from Client:Hello,Server! How are you ?
2 14269-14269/com.example.messageclient D/Messenger-Demo: receive msg from Server:Hello Client! I am fine, thank you

这样客户端和服务端就完成了一次互相通信。从代码上来看,就能感受到,相比于直接使用AIDL方式,Messenger简洁方便了很多。

三、Messenger的使用步骤流程图

通过前面的Demo直观感受了Messenger的使用,其交互流程大致为以下六步:

【朝花夕拾】跨进程通信,你只知道AIDL,就OUT了

对照Demo和上图,应该能够轻松理解Messenger的交互流程了。这里需要注意的是,实际上给Server端的Handler发送消息的Messenger,是结合服务端返回的IBinder实例来生成的服务端远程代理;给客户端Handler发送消息的Messenger也是第4步中发送给服务端的客户端本地Messenger, 可以理解为是自己的Messenger给自己的Handler在发送消息。

四、Messenger和AIDL的联系与区别

前面我们说过Messager的底层还是实现的AIDL,这是它们的联系。它们的区别是:

(1)Messenger使用起来比AIDL简洁方便。

(2)AIDL的客户端接口会同时向服务端发送多个请求,服务端需要应用多线程处理。而Messenger会将所有请求排入队列(Handler对应的MessageQueue),让服务器一次处理一个调用,不用处理多线程问题。大多数情况下,服务端不需要执行多线程处理此时选择Messenger方式更合适,而如果客户端的请求要求服务端执行多线程处理,就应该使用AIDL来实现,选择哪一种,还是需要根据实际情况来选择。

五、Messenger源码分析

到这里,就讲完了Messenger的基本使用方法,以及基本知识,但我们仍然需要分析源码来更深入理解Messenger,不能仅停留在应用的层面。下面我们大致按照按照第4点中的“客户端和服务端交互流程图”中的步骤,来分析一下源码。

1、Server端Messenger的创建以及onBinder回调方法返回值流程分析

当Client端通过bindService和Server端bind时,会回调用MessengerService中的onBind方法,并返回一个IBinder对象。

 //============MessengerService.java===========
private final Messenger mMessenger = new Messenger(new MessageHandler());
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}

第4行的mMessenger变量,它在第2行中创建,我们跟进到源码中看一下它的构造方法:

 //=============Messenger.java============
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();
} //==============Handler.java===========
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);
}
}

第3~10行的注释对理解Messenger很有用,这里简单翻译一下:创建一个新的指向给定Handler(也就是MessengerService类中的自定义的MessengerHandler)的Messenger。当Handler.sendMessage(Message)被直接调用时,任何通过该Messenger发送的Message对象都会出现在这个Handler(即MessengerHandler)中。参数是Handler 类型的targe,它将接收发送来的message。

通过上面的源码跟踪,可以发现,这里创建的Messenger,即mMessenger变量,实际上就是MessengerImpl对象。它继承自IMessenger.Stub,看到这里,我们就能够很容易联想到AIDL了,通过AIDL的方式,Service端回调的onBind方法,返回的自定义Binder就是就是这样写的。如果有编译过AIDL项目,跟进Stub后可以看到如下内容:

 public static abstract class Stub extends android.os.Binder implements xxx
//============Binder.java============
public class Binder implements IBinder {...}

Stub继承自android.os.Binder,可见MessengerImpl实际上也是一个Binder对象,而Binder是IBinder的实现类。现在我们就搞清楚了在创建Messenger对象时所创建的mTarget,实际上就是一个Binder。(这里讲到的AIDL相关的内容,可以参照文章开头处提到的文章,里面有讲到AIDL编译后生成的内容)。

再看mMessenger调用的getBinder()方法。

 //=============MessengerService.java=========

 public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
} //============Messenger.java=============
/**
* 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();
}

第7~13行注释有说明:该方法用于获取IBinder,当前Messenger(即mMessenger)正在用这个IBinder和与自己关联的Handler进行通信。这里仍然需要借助aidl生成的java接口文件中的内容(下面这个Stub在前面提到过)来理解:

 public static abstract class Stub extends android.os.Binder implements xxx {
......
@Override
public android.os.IBinder asBinder() {
return this;
}
......
}

通过前面的分析,我们已经知道了继承链:MessengerImpl extends IMessenger.Stub extends android.os.Binder implements IBinder。而mTarget是MessengerImpl的实例,我们可以得知,mTarget.asBinder()返回的就是MessengerImpl对象(这里不太确定这个结论是否正确,但返回的是Stub对象是显而易见的)。这里再次对比AIDL实现方式中onBind方法的返回形式,其常见写法是:return new MyBinder(),这里的MyBinder extends Stub,这样一对比,就和AIDL不谋而合了。

到这里,就分析完了Server端创建Messenger,以及回调onBinder方法的返回值源码流程。

2、Client端远程Messenger的创建

MessengerClientActivity类的第45行,bindService成功后,会创建一个用于和Server端发送消息的远程Messenger:

//======================MessengerClientActivity.java==================
1 public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteMessenger = new Messenger(service);
......
}

这里我们又看到了另外一种创建Messenger的方式,我们看看源码:

//=====================Messenger.java=================
1 /**
* 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);
}

我们仍然先翻译一下注释:从一个原始的IBinder创建一个Messenger,该IBinder之前通过getBinder方法来获取得到。参数为IBinder类型,当前Messenger应该和这个IBinder进行通信。当我们看到第8行的时候,是不是又仿佛看到了AIDL中客户端创建服务端代理类呢?

结合上述代码的分析,已经很明确地验证了第二节中说的,Messenger的底层仍然是AIDL!

3、Client端向Server端发送消息

在MessengerClientActivity类的第53行,也就是下面的第7行,就是远程Messenger向服务端发送消息。

//===============MessengerClientActivity.java=============
1 @Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteMessenger = new Messenger(service);
Message clientMsg = Message.obtain();
......
try {
mRemoteMessenger.send(clientMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

我们查看send的源码:

 //============Messenger.java===========
/**
* 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.java============
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}

我们仍然先简单看看注释:发送Message给该Messenger的Handler(该Messenger来自于Server端,所以这里的Handler也就是Server端自定义那个Handler)。第1点中讲过,mTarget就是MessengerImpl的实例,所以第12行就是执行的第17行。第19行我们再熟悉不过了,Handler发送Message,所以我们这里就很明确了,远程Messenger的send方法,实际上就是通过Handler来发送数据的。

4、Server端向Client端发送消息

实现Server端向Client端发送数据,关键代码如下:

 //=======  MessengerClientActivity========
private Messenger mLocalMessenger = new Messenger(new MessengerClientHandler());
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteMessenger = new Messenger(service);
Message clientMsg = Message.obtain();
......
clientMsg.replyTo = mLocalMessenger;
......
try {
mRemoteMessenger.send(clientMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
} //==========AidlService========
Messenger messenger = msg.replyTo;
Message serverMsg = Message.obtain();
......
try {
messenger.send(serverMsg);
} catch (RemoteException e) {
e.printStackTrace();
}

这里关键中的关键是第8行,和第18行。客户端需要将本地Messenger发送给Server端,也就是第8行,其源码如下:

 //==========Message.java===========
public Messenger replyTo;

这里的replyTo是一个Messenger类型的,第8行就将本地Messenger装进Message中,发送到Server端了。在Server端就会接收该Messenger,如第18行中所示。这样,Server端就拿到了Client端的本地Messenger对象,然后就可以通过这个Messenger给Client端发送消息了,接收者为Client端本地Messenger关联的Handler,这样就实现了服务端向客户端发送消息。

到这里,Messenger通信流程的源码分析就结束了。简单来说,Messenger原理就是封装了AIDL,以及使用Handler来发送消息。

结语

由于笔者水平有限,文章中如果有描述不准确或者不妥当的地方,还请读者不吝赐教,非常感谢!