­

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

  • 2019 年 11 月 1 日
  • 筆記

一、前言

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

 

二、Messenger简介

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

 

三、示例代码演示

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

  1、服务端代码实现

 1 public class MessengerService extends Service {   2     private static final String TAG = "Messenger-Demo";   3     private static final int MSG_CLIENT = 0x001;   4     private static final int MSG_SERVER = 0X002;   5     private static final String KEY_CLIENT = "key_client";   6     private static final String KEY_SERVER = "key_server";   7   8     private final Messenger mMessenger = new Messenger(new MessageHandler());   9  10     @Nullable  11     @Override  12     public IBinder onBind(Intent intent) {  13         return mMessenger.getBinder();  14     }  15  16     private static class MessageHandler extends Handler {  17         @Override  18         public void handleMessage(Message msg) {  19             switch (msg.what) {  20                 case MSG_CLIENT:  21                     Log.d(TAG, "receive msg from Client:" + msg.getData().getString(KEY_CLIENT));  22                     Messenger messenger = msg.replyTo;  23                     Message serverMsg = Message.obtain();  24                     serverMsg.what = MSG_SERVER;  25                     Bundle bundle = new Bundle();  26                     bundle.putString(KEY_SERVER, "Hello Client! I am fine, thank you");  27                     serverMsg.setData(bundle);  28                     try {  29                         messenger.send(serverMsg);  30                     } catch (RemoteException e) {  31                         e.printStackTrace();  32                     }  33                     break;  34                 default:  35                     super.handleMessage(msg);  36             }  37         }  38     }  39 }

View Code

       对应清单文件中的注册

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

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

  2、客户端代码实现

 1 public class MessengerClientActivity extends AppCompatActivity {   2   3     private static final String TAG = "Messenger-Demo";   4     private static final int MSG_CLIENT = 0x001;   5     private static final int MSG_SERVER = 0X002;   6     private static final String KEY_CLIENT = "key_client";   7     private static final String KEY_SERVER = "key_server";   8     private static final String SERVER_PKGNAME = "com.example.messageserver";   9     private static final String SERVICE_PATH = "com.example.messageserver.MessengerService";  10     private Messenger mRemoteMessenger;  11     private Messenger mLocalMessenger = new Messenger(new MessengerClientHandler());  12  13     @Override  14     protected void onCreate(Bundle savedInstanceState) {  15         super.onCreate(savedInstanceState);  16         setContentView(R.layout.activity_main);  17         Log.d(TAG,"onCreate");  18         bindService();  19     }  20  21     private void bindService() {  22         Intent intent = new Intent();  23         ComponentName componentName = new ComponentName(SERVER_PKGNAME, SERVICE_PATH);  24         intent.setComponent(componentName);  25         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);  26     }  27  28     private static class MessengerClientHandler extends Handler {  29         @Override  30         public void handleMessage(Message msg) {  31             switch (msg.what) {  32                 case MSG_SERVER:  33                     Log.d(TAG, "receive msg from Server:" + msg.getData().getString(KEY_SERVER));  34                     break;  35                 default:  36                     break;  37             }  38             super.handleMessage(msg);  39         }  40     }  41  42     private ServiceConnection mServiceConnection = new ServiceConnection() {  43         @Override  44         public void onServiceConnected(ComponentName name, IBinder service) {  45             mRemoteMessenger = new Messenger(service);  46             Message clientMsg = Message.obtain();  47             clientMsg.what = MSG_CLIENT;  48             Bundle bundle = new Bundle();  49             bundle.putString(KEY_CLIENT, "Hello,Server! How are you ?");  50             clientMsg.setData(bundle);  51             clientMsg.replyTo = mLocalMessenger;  52             try {  53                 mRemoteMessenger.send(clientMsg);  54             } catch (RemoteException e) {  55                 e.printStackTrace();  56             }  57         }  58  59         @Override  60         public void onServiceDisconnected(ComponentName name) {  61  62         }  63     };  64  65     @Override  66     protected void onDestroy() {  67         super.onDestroy();  68         unbindService(mServiceConnection);  69     }  70 }

View Code

  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的使用,其交互流程大致为一下六步:

 

 

       对照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来实现,选择哪一种,还是需要根据实际情况来选择。

 

结语

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