💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
#### 2.4.3 使用Messenger **Messenger可以翻译为信使,顾名思义,通过它可以在不同进程中传递Message(信息)对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了**。 Messenger是一种**轻量级的IPC方案**,它的**底层实现是AIDL**,为什么这么说呢,我们大致看一下Messenger这个类的构造方法就明白了。下面是Messenger的两个构造方法,**从构造方法的实现上我们可以明显看出AIDL的痕迹,不管是IMessenger还是Stub.asInterface,这种使用方法都表明它的底层是AIDL**。 ``` public Messenger(Handler target) { mTarget = target.getIMessenger(); } public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); } ``` **Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简便地进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形**。 ***** 总之:**Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了,同时,Messenger的作用主要是为了*传递消息*,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了**。 ***** 实现一个Messenger有如下几个步骤,分为服务端和客户端,这里的可不是我们平常意义上的客户端和服务端,可以理解为两个进程。 **1.服务端进程** 首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。 **2.客户端进程** 客户端进程中,首先要绑定服务端的Service(onCreate方法中调用bindService方法),绑定成功后用服务端返回的IBinder对象(ServiceConnection方法中返回)创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。 这听起来可能还是有点抽象,不过看了下面的两个例子,读者肯定就都明白了。首先,我们来看一个简单点的例子,在这个例子中服务端无法回应客户端。 首先看服务端的代码,这是服务端的典型代码,可以看到**MessengerHandler用来处理客户端发送的消息,并从消息中取出客户端发来的文本信息**。而**mMessenger是一个Messenger对象,它和MessengerHandler相关联,并在onBind方法中返回它里面的Binder对象**,可以看出,这里**Messenger的作用是将客户端发送的消息传递给MessengerHandler处理**。 * **服务端代码** ~~~ public class MessengerService extends Service { private static final String TAG = "MessengerService"; //继承Handler,MessengerHandler用来处理客户端发送的消息,并从消息中取出客户端发来的文本信息。 private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MyConstants.MSG_FROM_CLIENT: Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg")); /** * 因为我们的Messenger是通过客户端来获取的,而在客户端那边这个Messenger是以Handler为参数创建的, * 所以在服务端通过客户端的Messenger发送消息后,在客户端的Handler就会处理这条消息, * 嘻嘻,就达到了消息传送的目的。 */ Messenger client = msg.replyTo; Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE); Bundle bundle = new Bundle(); bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。"); relpyMessage.setData(bundle); try { client.send(relpyMessage); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } //这是我们服务端自己的Messenger,它是以上面的Handler对象为参数创建的, //这个Messenger是要通过绑定该服务器的时候onBind方法传递给客户端,然后客户端获取了该Messenger, //再以该Messenger来发送消息,这样服务端就可以接收到该消息并处理。 private final Messenger mMessenger = new Messenger(new MessengerHandler()); //这个方法是在绑定服务的过程中调用的并将结果返回给客户端的,所以通过onBind方法客户端就可以获取我们Messenger的Binder对象了, //然后客户端可以根据该Binder对象来创建一个Messenger,这样客户端中用的Messenger和这里的Messenger就是向对应的了。 @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } } ~~~ 然后,注册service,让其运行在单独的进程中: ``` <service android:name="com.ryg.chapter_2.messenger.MessengerService" android:process=":remote" > ``` 接下来再看看客户端的实现,客户端的实现也比较简单,首先需要绑定远程进程的MessengerService,绑定成功后,根据服务端返回的binder对象创建Messenger对象并使用此对象向服务端发送消息。下面的代码在Bundle中向服务端发送了一句话,在上面的服务端代码中会打印出这句话。 **客户端代码** ``` public class MessengerActivity extends Activity { private static final String TAG = "MessengerActivity"; private Messenger mService; //准备一个接收消息的Messenger和Handler //这是客户端自己的Messenger,传递给服务端,让服务端返回消息用的。 private Messenger mGetReplyMessenger = new Messenger (new MessengerHandler ()); private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MyConstants.MSG_FROM_SERVICE: Log.i (TAG, "receive msg from Service:" + msg.getData ().getString ("reply")); break; default: super.handleMessage (msg); } } } /** * 这个是客户端用来绑定服务端用的,在绑定过程中会调用onServiceConnected,它的第二个参数IBinder service, * 就是在服务端中onBind方法返回的结果,这个结果是服务端的Messenger对象的Binder对象, * 然后客户端通过这个Binder对象就可以创建一个Messenger, * 所以就是在绑定服务的过程中将服务端的Messenger传递给了客户端,建立起了两者之间的桥梁 */ private ServiceConnection mConnection = new ServiceConnection () { public void onServiceConnected(ComponentName className, IBinder service) { mService = new Messenger (service); Log.d (TAG, "bind service"); Message msg = Message.obtain (null, MyConstants.MSG_FROM_CLIENT); Bundle data = new Bundle (); data.putString ("msg", "hello, this is client."); msg.setData (data); //把接收服务端回复的Messenger通过Message的replyTo参数传递给服务端 msg.replyTo = mGetReplyMessenger; try { mService.send (msg); } catch (RemoteException e) { e.printStackTrace (); } } public void onServiceDisconnected(ComponentName className) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.activity_messenger); Intent intent = new Intent ("com.ryg.MessengerService.launch"); //在bindService的时候,服务端会通过onBind方法返回一个包含了服务端业务调用的Binder对象, //通过这个对象,客户端就可以获取服务端提供的服务或者数据, bindService (intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService (mConnection); super.onDestroy (); } } ``` 最后,我们运行程序,看一下log,很显然,服务端成功收到了客户端所发来的问候语:“hello, this is client.”。 ``` I/MessengerService( 1037): receive msg from Client:hello, this is client. ``` 通过上面的例子可以看出,**在Messenger(信使)中进行数据传递必须将数据放入Message(信息)中,而Messenger和Message都实现了Parcelable接口,因此可以跨进程传输**。简单来说,**Message中所支持的数据类型就是Messenger所支持的传输类型**。实际上,**通过Messenger来传输Message,Message中能使用的载体只有what、arg1、arg2、Bundle以及replyTo。Message中的另一个字段object在同一个进程中是很实用的,但是在进程间通信的时候,在Android 2.2以前object字段不支持跨进程传输,即便是2.2以后,也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输。这就意味着我们自定义的Parcelable对象是无法通过object字段来传输的**,读者可以试一下。**非系统的Parcelable对象的确无法通过object字段来传输,这也导致了object字段的实用性大大降低**,所幸我们还有Bundle,**Bundle中可以支持大量的数据类型**。 上面的例子演示了如何在服务端接收客户端中发送的消息,但是有时候我们还需要能回应客户端,下面就介绍如何实现这种效果。还是采用上面的例子,但是稍微做一下修改,每当客户端发来一条消息,服务端就会自动回复一条“嗯,你的消息我已经收到,稍后会回复你。”,这很类似邮箱的自动回复功能。 首先看服务端的修改,服务端只需要修改MessengerHandler,当收到消息后,会立即回复一条消息给客户端。 ``` //继承Handler,MessengerHandler用来处理客户端发送的消息,并从消息中取出客户端发来的文本信息。 private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MyConstants.MSG_FROM_CLIENT: Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg")); /** * 因为我们的Messenger是通过客户端来获取的,而在客户端那边这个Messenger是以Handler为参数创建的, * 所以在服务端通过客户端的Messenger发送消息后,在客户端的Handler就会处理这条消息, * 嘻嘻,就达到了消息传送的目的。 */ Messenger client = msg.replyTo; Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE); Bundle bundle = new Bundle(); bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。"); relpyMessage.setData(bundle); try { client.send(relpyMessage); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } ``` 接着再看客户端的修改,为了接收服务端的回复,客户端也需要准备一个接收消息的Messenger和Handler,如下所示。 ``` //准备一个接收消息的Messenger和Handler //这是客户端自己的Messenger,传递给服务端,让服务端返回消息用的。 private Messenger mGetReplyMessenger = new Messenger (new MessengerHandler ()); private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MyConstants.MSG_FROM_SERVICE: Log.i (TAG, "receive msg from Service:" + msg.getData ().getString ("reply")); break; default: super.handleMessage (msg); } } } ``` 除了上述修改,还有很关键的一点,当客户端发送消息的时候,需要把接收服务端回复的Messenger通过Message的replyTo参数传递给服务端,如下所示。 ``` private ServiceConnection mConnection = new ServiceConnection () { public void onServiceConnected(ComponentName className, IBinder service) { mService = new Messenger (service); Log.d (TAG, "bind service"); Message msg = Message.obtain (null, MyConstants.MSG_FROM_CLIENT); Bundle data = new Bundle (); data.putString ("msg", "hello, this is client."); msg.setData (data); //把接收服务端回复的Messenger通过Message的replyTo参数传递给服务端 msg.replyTo = mGetReplyMessenger; try { .send (msg); } catch (RemoteException e) { e.printStackTrace (); } } public void onServiceDisconnected(ComponentName className) { } }; ``` 通过上述修改,我们再运行程序,然后看一下log,很显然,客户端收到了服务端的回复“嗯,你的消息我已经收到,稍后会回复你。”,这说明我们的功能已经完成。 ``` I/MessengerService( 1419): receive msg from Client:hello, this is client. I/MessengerActivity( 1404): receive msg from Service:嗯,你的消息我已经收到, ``` 稍后会回复你。 到这里,我们已经把采用Messenger进行进程间通信的方法都介绍完了,读者可以试着通过Messenger来实现更复杂的跨进程通信功能。下面给出一张Messenger的工作原理图以方便读者更好地理解Messenger,如图2-6所示。 :-: ![](https://img.kancloud.cn/7f/9a/7f9afd1a086ab10578c59554d4f14dcc_1437x530.png) 图2-6 Messenger的工作原理 关于进程间通信,可能有的读者会觉得笔者提供的示例都是针对同一个应用的,有没有针对不同应用的?是这样的,之所以选择在同一个应用内进行进程间通信,是因为操作起来比较方便,但是效果和在两个应用间进行进程间通信是一样的。在本章刚开始就说过,**同一个应用的不同组件,如果它们运行在不同进程中,那么和它们分别属于两个应用没有本质区别**,关于这点需要深刻理解,因为这是理解进程间通信的基础。