[转载]AsyncChannel的使用和原理

转自 https://blog.csdn.net/u010961631/article/details/48179305#t13

        本文主要介绍AsyncChannel的使用和内部原理。


一、AsyncChannel概述

        

        AsyncChannel在Wifi的处理机制中被大量使用,但是可惜的是该工具是一个internal的方法,没有开放给第三方应用使用,但是该工具提供的思路很有用,可以被扩展到任意两个Handle间传输数据的通道。

        那么AsyncChannel究竟是什么工具呢?他的内部机制又是怎么样呢?
        我们先来看一下AsyncChannel的官方描述:

 
 
  1. * <p>An asynchronous channel between two handlers. </p>
  2. * <p>The handlers maybe in the same process or in another process. There
  3. * are two protocol styles that can be used with an AysncChannel. The
  4. * first is a simple request/reply protocol where the server does
  5. * not need to know which client is issuing the request. </p>
  6. * <p>In a simple request/reply protocol the client/source sends requests to the
  7. * server/destination. And the server uses the replyToMessage methods.
  8. * In this usage model there is no need for the destination to
  9. * use the connect methods. The typical sequence of operations is: </p>
  10. * <p>A second usage model is where the server/destination needs to know
  11. * which client it's connected too. For example the server needs to
  12. * send unsolicited messages back to the client. Or the server keeps
  13. * different state for each client. In this model the server will also
  14. * use the connect methods. The typical sequence of operation is: </p>
        以上是AsyncChannel文件头的描述,从这三段描述中我们可以大致知道AsyncChannel的作用、特点以及用法。

1.1、作用

        AsyncChannel在两个Handler间搭建了通道,可以用于消息传输。


1.2、特点


        从上面第一段英文描述可以知道AsyncChannel的两个特点:

        1、AsyncChannel可以作为Handler之间的通道。

        2、这两个Handler可以处于同一个进程,也可以不再同一个进程。


1.3、两种工作模式


        第二段和第三段英文分别描述了AsyncChannel的两种工作模式:

        1、单项通道模式,在该模式下,客户端只能向服务端发起请求,服务端给出回应。
        2、双向通道模式,在该模式下,客户端和服务端同时连接上AsyncChannel,客户端可以向服务端发送请求,服务端也可以向客户端发送请求。

        下面我们分别来介绍这两种模式的使用方法。


二、AsyncChannel通道使用方法

        

        为了进行测试,我搭建了一个Demo程序用于测试相关功能,源码可以这里下载。由于AsyncChannel是internal的工具,因此该Demo必须要在ROM源码环境下编译并具备系统签名

        在这个Demo中,只有三个文件:MainActivity、AsyncChannelClient、AsyncChannelService、AsyncChannelServiceFull四个文件,其作用分别是:
        MainActivity
            ----负责界面搭建,里面包含:“初始化客户端和服务端并依次建立单向通道和双向通道”、“初始化客户端和服务端并直接建立双向通道”、“发送异步信息”、“发送同步信息”等四个按钮。
        AsyncChannelClient
            ----作为客户端,向服务端发送请求。
        AsyncChannelService
            ----作为服务端,接受客户端请求。
        AsyncChannelServiceFull
            ----作为服务端,用于接受快速双向通道的请求。

        下面来看AsyncChannel的具体使用。


2.1、单向通道建立方法

        

        在这个Demo中,当点击“初始化客户端和服务端并依次建立单向通道和双向通道”后,就会先后启动服务端和客户端,当客户端的Server启动(onStartCommand)时,就会与服务端建立单项通道。

        我们先来看点击动作的处理:

 
 
  1. @MainActivity. java
  2. public void onClick (View view) {
  3. switch (view.getId()) {
  4. case R.id.start_service_btn: {
  5. // 启动客户端和服务端
  6. Intent intent = new Intent();
  7. intent.setComponent( new ComponentName( this, AsyncChannelService.class));
  8. startService(intent);
  9. Intent inte = new Intent();
  10. inte.setComponent( new ComponentName( this, AsyncChannelClient.class));
  11. //Action是"Normal"
  12. inte.setAction( "Normal");
  13. startService(inte);
  14. break;
  15. }
  16. }
  17. }
        然后我们来看客户端的 初始化流程:

 
 
  1. @AsyncChannelClient.java
  2. public static AsyncChannel sClientAsyncChannel;
  3. public int onStartCommand(Intent intent, int flags, int startId) {
  4. //创建本地的Handler对象
  5. HandlerThread handlerThread = new HandlerThread( "ClientThread");
  6. handlerThread.start();
  7. Handler clientHandler = new ClientHandler(handlerThread.getLooper());
  8. //创建客户端的AsyncChannel
  9. sClientAsyncChannel = new AsyncChannel();
  10. String act = intent.getAction();
  11. //我们发送的Action就是"Normal"
  12. if ( "Normal".equals(act)) {
  13. // 建立单向通道,获取服务端的Messenger对象
  14. Messenger serviceMessenger = AsyncChannelService.getServiceMessenger();
  15. //发起连接请求
  16. sClientAsyncChannel.connect( this, clientHandler, serviceMessenger);
  17. } else if ( "Fast".equals(act)) {
  18. }
  19. return super.onStartCommand(intent, flags, startId);
  20. }
        上面的过程中,通过客户端的getServiceMessenger()方法 拿到服务端的Messenger对象,其过程是:

 
 
  1. @AsyncChannelService. java
  2. public static Messenger getServiceMessenger () {
  3. //服务端需要用自己的Handler构建Messenger然后发送给客户端
  4. return new Messenger(mServiceHandler);
  5. }
        以上就是客户端所做的全部准备,以及发起创建通道的请求过程。从这个过程中我们看到,作为客户端,如果想要创建与服务端之间的AsyncChannel,需要做以下几个准备:
        1、获取服务端的Messenger对象,该对象其实就是利用服务端的Handler构建的Messenger;
        2、创建客户端自己的Handler对象;
        3、创建AsyncChannel对象;
        4、通过AsyncChannel对象,连接当前的Handler和服务端的Messenger,从而申请连接。

        当客户端发送完connect()请求之后,客户端需要做的就是在Handler对象中等待Message消息:

 
 
  1. @AsyncChannelClient. java
  2. public void handleMessage (Message message) {
  3. switch (message.what) {
  4. case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
  5. if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
  6. Log.d(tag, "Service half connected");
  7. }
  8. break;
  9. }
  10. }
  11. }
        当客户端接收到AsyncChannel.CMD_CHANNEL_HALF_CONNECTED消息后,说明当前的单项通道建立成功。此时的客户端可以通过sAsyncChannel的sendMessage()方法向服务端发送消息了。

        以上就是单项通道的建立过程,整个过程不需要服务端做任何操作。


2.2、双向通道建立方法


        那么如果客户端想要创建双向通道,该如何操作呢?

        首先,服务端本身需要初始化Handler,并且如果服务端准备提供双向通道,那么就需要创建自己的AsyncChannel对象。这些工作需要在客户端初始化时完成:

 
 
  1. @AsyncChannelService. java
  2. public int onStartCommand (Intent intent, int flags, int startId) {
  3. //初始化Handler
  4. HandlerThread handlerThread = new HandlerThread( "ServiceThread");
  5. handlerThread.start();
  6. mServiceHandler = new ServiceHandler(handlerThread.getLooper());
  7. //创建AsyncChannel对象
  8. sServiceAsyncChannel = new AsyncChannel();
  9. return super.onStartCommand(intent, flags, startId);
  10. }
        其实 双向通道是在单项通道基础上完成的,当客户端与服务端的单项通道创建完成后,也就是 当客户端收到AsyncChannel.CMD_CHANNEL_HALF_CONNECTED之后,可以向AsyncChannel对象发送CMD_CHANNEL_FULL_CONNECTION消息申请建立双向通道
        也就是在刚才接收到的消息基础上,发送CMD_CHANNEL_FULL_CONNECTION请求即可:

 
 
  1. @AsyncChannelClient. java
  2. public void handleMessage (Message message) {
  3. switch (message.what) {
  4. case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
  5. if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
  6. //客户端单项通道建立完成,继续申请双向通道
  7. sClientAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
  8. }
  9. break;
  10. }
  11. }
        此时的服务端将会在Handler中接收到如下消息:

 
 
  1. @AsyncChannelService. java
  2. public void handleMessage (Message msg) {
  3. switch ((msg.what)) {
  4. case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
  5. //服务端接收到双向通道的建立请求
  6. //如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
  7. sServiceAsyncChannel.connect(AsyncChannelService. this, mServiceHandler, msg.replyTo);
  8. break;
  9. }
  10. }
  11. }
        从上面代码看到,当客户端发起双向通道建立请求时,服务端将会收到AysncChannel.CMD_CHANNEL_FULL_CONNECTION的请求,此时的服务端如果同意建立双向通道,则使用自己的AsyncChannel对象将自己的Handler与客户端的Handler连接起来。
        至此,双向通道建立完成,此时不仅客户端可以通过自己的AsyncChannel对象向服务端发送请求,服务端也可以通过自己的AsyncChannel对象向客户端发送请求。

        以上就是双向通道的建立过程。


2.3、更有效的双向通道建立方法


        上一节中介绍了双向通道的建立方法,其实这是一种“笨方法”,为什么这么说呢?我们来看一下要创建双向通道需要经过哪些步骤:

        1、客户端发起connect()请求,申请建立单向通道;
        2、客户端接收到CMD_CHANNEL_HALF_CONNECTED,然后继续向AsyncChannel申请双向通道;
        3、服务端接收到CMD_CHANNEL_FULL_CONNECTION申请,向AsyncChannel发送连接请求;
        这就需要三个步骤才可以完成双向通道建立,并且最重要的是, 客户端始终不知道服务端是否真正连接上了AsyncChannel(如果客户端想知道结果,还需要服务端再次发送一次确认消息)。
        那么有没有一种方法,在客户端创建通道时就指定要创建双向通道呢?
        这就用上 fullyConnectSync()方法。通过该方法,客户端只需要一次就可以申请到双向通道,并且可以知道通道是否建立成功。
        下面来看代码实现,当点击界面上“初始化客户端与服务端并快速建立双向通道”按钮后,将会触发客户端双向通道的申请:

 
 
  1. @MainActivity. java
  2. public void onClick (View view) {
  3. switch (view.getId()) {
  4. case R.id.start_service_fast_connect_btn: {
  5. Intent intent = new Intent();
  6. intent.setComponent( new ComponentName( this, AsyncChannelServiceFull.class));
  7. startService(intent);
  8. Intent inte = new Intent();
  9. inte.setComponent( new ComponentName( this, AsyncChannelClient.class));
  10. inte.setAction( "Fast");
  11. startService(inte);
  12. break;
  13. }
  14. }
  15. }
        此时将会先后启动AsyncChannelServiceFull和AsyncChannelClient服务,并且发送的Action为“Fast”。
        我们先来看AsyncChannelServiceFull服务的初始化流程:

 
 
  1. @AsyncChannelServiceFull. java
  2. public int onStartCommand (Intent intent, int flags, int startId) {
  3. //初始化Handler对象
  4. HandlerThread handlerThread = new HandlerThread( "ServiceThread");
  5. handlerThread.start();
  6. mServiceHandler = new ServiceHandler(handlerThread.getLooper());
  7. //创建AsyncChannel对象
  8. sServiceAsyncChannel = new AsyncChannel();
  9. return super.onStartCommand(intent, flags, startId);
  10. }
        然后来看AsyncChannelClient的操作:

 
 
  1. @AsyncChannelClient. java
  2. public int onStartCommand (Intent intent, int flags, int startId) {
  3. //初始化Handler对象
  4. HandlerThread handlerThread = new HandlerThread( "ClientThread");
  5. handlerThread.start();
  6. Handler clientHandler = new ClientHandler(handlerThread.getLooper());
  7. //创建AsyncChannel对象
  8. sClientAsyncChannel = new AsyncChannel();
  9. String act = intent.getAction();
  10. if ( "Normal".equals(act)) {
  11. } else if ( "Fast".equals(act)) { //此时的Action是"Fast"
  12. // 要求直接双向通道
  13. Handler serviceHandler = AsyncChannelServiceFull.getServiceHandler();
  14. int result = sClientAsyncChannel.fullyConnectSync( this, clientHandler, serviceHandler);
  15. if (AsyncChannel.STATUS_SUCCESSFUL == result) {
  16. Log.d(tag, "client full connected");
  17. }
  18. }
  19. return super.onStartCommand(intent, flags, startId);
  20. }
        这与单项通道的建立操作几乎完全一样,唯一不同的地方有三个:
        1、客户端不再使用connect()方法连接AsyncChannel,而是使用fullyConnectSync()方法建立双向通道;
        2、调用fullyConnectSync()建立通道时,使用的是服务端的Handler对象(serviceHandler)而不是Messenger对象;
        3、调用fullyConnectSync()建立通道时,此方法有个返回值,可以明确知道通道是否建立成功;

        接下来我们看此时的服务端的流程。
        当客户端通过fullyConnectSync()申请双向通道时,服务端就可以收到CMD_CHANNEL_FULL_CONNECTION的消息,然后服务端需要连接上AsyncChannel后回复成功的命令即可完成通道的建立:

 
 
  1. @AsyncChannelServiceFull. java
  2. public void handleMessage (Message msg) {
  3. switch (msg.what) {
  4. case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
  5. //收到双向通道建立请求,如果服务端同意,则需要连接上AsyncChannel,然后恢复STATUS_SUCCESSFUL即可
  6. sServiceAsyncChannel.connect(AsyncChannelServiceFull. this, mServiceHandler, msg.replyTo);
  7. //将建立结果传递给客户端
  8. sServiceAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_SUCCESSFUL);
  9. break;
  10. }
  11. }
  12. }
        经过上面服务端的操作,客户端就在fullyConnectSync()返回值中获取到通道建立成功的消息,至此双向通道建立完毕。
        从这个过程中我们看到该方式更加方便和有效, 他省掉了客户端CMD_CHANNEL_HALF_CONNECTED的请求过程,而且客户端可以知道通道的建立结果。这也是官方推荐的做法。 

        以上就是快速双向通道的建立过程。


三、消息的使用


        我们建立通道的最终目的是为了在两个Handler间传输信息,而消息又分为两类:异步消息和同步消息,接下来分别介绍这两种消息的使用方法。


3.1、异步消息使用方法


        所谓异步消息,就是客户端(或服务端)发送消息之后在Handler中等待回应,而不是立刻拿到消息结果。无论是客户端或者是双向通道中的服务端,都可以通过自己的AsyncChannel对象向对方发送异步消息。

        在该Demo中,当点击界面上的“发送异步信息”按钮时,就会在客户端中向服务端发送一个MainActivity.MSG_ASYNC_REQ的请求,接下来在Server端的handleMessage中就能收到该请求,我们在Server收到该请求后,创建一个MSG_SYNC_REPLY的消息,并在其中放入一个字串“ServiceAsyncReply”返回给客户端,接下来客户端就可以在自己的handleMessage中检测到该回应,最终我们在客户端的handleMessage中把该字串打印出来。
        接下来看具体的代码:
        当点击“客户端发送异步消息”的按钮时,进入客户端的sendMsgToService()方法中:

 
 
  1. @AsyncChannelClient. java
  2. public static void sendMsgToService () {
  3. // 客户端用AsyncChannel给服务端发送MSG_ASYNC_REQ的请求
  4. sClientAsyncChannel.sendMessage(MainActivity.MSG_ASYNC_REQ);
  5. }
        接下来就会在服务端的handleMessage中接收到该请求:

 
 
  1. @AsyncChannelService. java
  2. public void handleMessage (Message msg) {
  3. switch (msg.what) {
  4. case MainActivity.MSG_ASYNC_REQ: {
  5. //接收到客户端请求
  6. Message reply = msg.obtain();
  7. reply.what = MainActivity.MSG_ASYNC_REPLY;
  8. reply.obj = "ServiceAsyncReply";
  9. try {
  10. //给客户端发送回应,回应的内容中what=MSG_ASYNC_REPLY,obj="ServiceAsyncReply"
  11. msg.replyTo.send(reply);
  12. } catch (RemoteException e) {
  13. }
  14. break;
  15. }
  16. }
        然后客户端就会收到MSG_ASYNC_REPLY的回应:

 
 
  1. @AsyncChannelClient. java
  2. public void handleMessage (Message message) {
  3. switch (message.what) {
  4. case MainActivity.MSG_ASYNC_REPLY: {
  5. //收到服务端回应,并将服务端放入的字串打印出来,异步消息结束
  6. String msg = (String) message.obj;
  7. Log.d(tag, "get service async reply msg = " + msg);
  8. break;
  9. }
  10. }
  11. }

        至此就完成了整个异步消息的调用。


3.2、同步消息使用方法


        从上面的异步消息看到,无论是客户端发送消息还是服务端返回消息,都是异步的,都需要在发送完毕后被动等待消息结果,而同步消息就是客户端(或服务端)发送消息之后可以直接拿到返回值。

        接下来我们通过Demo来演示同步消息的使用方法。
        在该Demo中,当点击“发送同步信息”按钮后,客户端将会给服务端发送一个“MSG_SYNC_REQ”的同步消息,然后等待返回值,此时的Server端将会收到该消息,等服务端处理完毕后通过replyToMessage()返回后,客户端就收到了返回值,整个过程对于客户端来说,是同步的,也就是说调用后立刻拿到了消息回应。
        我们先来看客户端发送同步信息的方式:

 
 
  1. @AsyncChannelClient. java
  2. public static void sendSyncMsgToService () {
  3. Message replyMsg = sClientAsyncChannel.sendMessageSynchronously(MainActivity.MSG_SYNC_REQ);
  4. String msg = (String) replyMsg.obj;
  5. Log.d(tag, "get service sync reply msg = " + msg);
  6. }
        从这里的发送过程就可以看出,对于发送端来说,“好像”发送完请求后,立刻得到了消息。当调用AsyncChannel的sendMessageSynchronously()方法后,服务端将会接收到该消息,并进行消息处理:

 
 
  1. @AsyncChannelService. java
  2. public void handleMessage (Message msg) {
  3. switch (msg.what) {
  4. case MainActivity.MSG_SYNC_REQ: {
  5. //服务端接收到同步消息请求
  6. Message reply = msg.obtain();
  7. reply.what = MainActivity.MSG_SYNC_REPLY;
  8. reply.obj = "ServiceSyncReply";
  9. //将返回值发送给客户端
  10. sServiceAsyncChannel.replyToMessage(msg, reply);
  11. break;
  12. }
  13. }
  14. }
        从服务端消息处理可以看到,服务端接收到MSG_SYNC_REQ请求后,包装了一个reply的Message对象,然后通过服务端的AsyncChannel对象将该消息返回给消息的发送者。然后客户端就收到了服务端的回应。

        这就是同步消息的使用方法。


四、AsyncChannel通道的内部机制


        以上通过Demo介绍了AsyncChannel的使用方法,接下来我们介绍AsyncChannel的内部原理。


4.1、单向通道的建立过程


        从前面介绍的通道使用方法来看,第一种双向通道是在单向通道基础上搭建的,我们先来看单项通道的建立过程。

        从通道的建立方法可以知道,创建通道需要三个步骤:
        1、准备服务端的Messenger对象;
        2、创建本地的Handler对象;
        3、创建AsyncChannel对象,然后调用connect()方法。
        其中前两个条件容易满足,下面我们主要分析AsyncChannel的创建过程和connect()方法。
        我们先来看一下AsyncChannel的属性:
        public class AsyncChannel {}
 
 
        发现他没有父类,然后看他的构造方法:

 
 
  1. @AsyncChannel. java
  2. public AsyncChannel () {
  3. }
        构造方法里面也没有任何操作。然后来看connect()方法:

 
 
  1. public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
  2. connected(srcContext, srcHandler, dstMessenger);
  3. replyHalfConnected(STATUS_SUCCESSFUL);
  4. }
        这里面有两个调用,我们先来看第一个connected()方法:

 
 
  1. public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
  2. //初始化一些变量
  3. mSrcContext = srcContext;
  4. mSrcHandler = srcHandler;
  5. mSrcMessenger = new Messenger(mSrcHandler);
  6. mDstMessenger = dstMessenger;
  7. }
        在这里只是初始化一些变量,我们记得在使用AsyncChannel时,传递了本地的Handler对象和目标的Messenger对象,在这里用本地的Handler对象创建Messenger对象,也就是说, 在AsyncChannel中,拥有了本地和远端的Messenger对象,分别标记mSrcMessenger和mDeathMonitor
        然后我们接着看connect()中的第二个方法:

 
 
  1. private void replyHalfConnected(int status) {
  2. //准备CMD_CHANNEL_HALF_CONNECTED消息
  3. Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
  4. msg.arg1 = status;
  5. msg.obj = this;
  6. msg.replyTo = mDstMessenger;
  7. if (mConnection == null) {
  8. mDeathMonitor = new DeathMonitor();
  9. try {
  10. //监听远端的服务是否死掉
  11. mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
  12. } catch (RemoteException e) {
  13. mDeathMonitor = null;
  14. msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
  15. }
  16. }
  17. //将CMD_CHANNEL_HALF_CONNECTED发送给本地,也就是客户端
  18. mSrcHandler.sendMessage(msg);
  19. }
        在这里主要完成了一件事情,也就是 向客户端发送CMD_CHANNEL_HALF_CONNECTED的消息
        然后AsyncChannel的connect()就调用结束,他的调用过程可以分为两步:
        1、初始化AsyncChannel中的本地和远程的Messenger对象;
        2、向本地(也就是客户端)发送CMD_CHANNEL_HALF_CONNECTED的消息;

        至此,单项通道建立完毕。
        该流程可以用如下图来描述:

        


4.2、双向通道的建立过程


        下面介绍双向通道的建立过程。前面的使用方法中我们知道,双向通道的建立需要在客户端收到CMD_CHANNEL_HALF_CONNECTED消息后向AsyncChannel对象发送CMD_CHANNEL_FULL_CONNECTION消息完成的,也就是如下的入口:


 
 
  1. public void handleMessage(Message message) {
  2. switch (message.what) {
  3. case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
  4. //向AsyncChannel申请双向通道
  5. sClientAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
  6. break;
  7. }
  8. }
  9. }
        接下来看AsyncChannel的sendMessage()过程:

 
 
  1. @AsyncChannel. java
  2. public void sendMessage (int what) {
  3. Message msg = Message.obtain();
  4. msg.what = what;
  5. sendMessage(msg);
  6. }
  7. public void sendMessage(Message msg) {
  8. msg.replyTo = mSrcMessenger;
  9. try {
  10. //向mDstMessenger发送消息
  11. mDstMessenger.send(msg);
  12. } catch (RemoteException e) {
  13. replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
  14. }
  15. }
        从这里看到,AsyncChannel其实只做了一件事情,就是将CMD_CHANNEL_FULL_CONNECTION的消息转发给mDstMessenger,这里的mDstMessenger就是AsyncChannel被创建时初始化的AsyncChannelService中的Handler,然后服务端就收到了CMD_CHANNEL_FULL_CONNECTION的消息。
        从前面的使用方法中介绍过,当服务端收到该消息后,将会通过connect()方法将自己连接上AsyncChannel:

 
 
  1. @AsyncChannelService. java
  2. public void handleMessage (Message msg) {
  3. switch ((msg.what)) {
  4. case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
  5. //服务端接收到双向通道的建立请求
  6. //如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
  7. sServiceAsyncChannel.connect(AsyncChannelService. this, mServiceHandler, msg.replyTo);
  8. break;
  9. }
  10. }
  11. }
        然后服务端的AsyncChannel将会进入connect()中处理:

 
 
  1. @AsyncChannel. java
  2. public void connect (Context srcContext, Handler srcHandler, Messenger dstMessenger) {
  3. //做同样的初始化动作
  4. connected(srcContext, srcHandler, dstMessenger);
  5. //然后告诉服务端CMD_CHANNEL_HALF_CONNECTED
  6. replyHalfConnected(STATUS_SUCCESSFUL);
  7. }
        这个过程与建立单向通道时客户端所做的动作完全一致,就是初始化服务端AsyncChannel中的mSrcMessenger、mSrcHandler、mDstMessenger等对象,然后向服务端自己发送CMD_CHANNEL_HALF_CONNECTED的消息。
        从这个过程来看,其实双向通道的建立过程就是在客户端和服务端分别连接(connect)上自己的AsyncChannel对象的过程。
        下面就是双向通道的建立流程图:

        


4.3、更有效的双向通道的建立过程


        下面我们来看快速双向通道的建立过程。

        该过程是通过客户端使用fullyConnectSync()方法来触发的:

 
 
  1. @AsyncChannel. java
  2. public int fullyConnectSync (Context srcContext, Handler srcHandler, Handler dstHandler) {
  3. int status = connectSync(srcContext, srcHandler, dstHandler);
  4. if (status == STATUS_SUCCESSFUL) {
  5. Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
  6. status = response.arg1;
  7. }
  8. return status;
  9. }
        这个方法内部有两个调用,一个connectSync、一个是sendMessageSynchronously,我们先来追踪第一个:

 
 
  1. public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
  2. return connectSync(srcContext, srcHandler, new Messenger(dstHandler));
  3. }
  4. public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
  5. connected(srcContext, srcHandler, dstMessenger);
  6. return STATUS_SUCCESSFUL;
  7. }
  8. public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
  9. //初始化变量
  10. mSrcContext = srcContext;
  11. mSrcHandler = srcHandler;
  12. mSrcMessenger = new Messenger(mSrcHandler);
  13. mDstMessenger = dstMessenger;
  14. }
        整个过程的结果就是调用connected()方法,这与connect()唯一不同的就是 没有发送CMD_CHANNEL_HALF_CONNECTED消息
        然后来看sendMessageSynchronously()方法,记得此时传递的消息是CMD_CHANNEL_FULL_CONNECTION:

 
 
  1. public Message sendMessageSynchronously(int what) {
  2. Message msg = Message.obtain();
  3. msg.what = what;
  4. Message resultMsg = sendMessageSynchronously(msg);
  5. return resultMsg;
  6. }
        继续:

 
 
  1. public Message sendMessageSynchronously(Message msg) {
  2. Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
  3. return resultMsg;
  4. }
        继续:

 
 
  1. private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
  2. //创建SyncMessenger对象
  3. SyncMessenger sm = SyncMessenger.obtain();
  4. try {
  5. if (dstMessenger != null && msg != null) {
  6. //将SyncMessenger的Messenger放入消息包中
  7. msg.replyTo = sm.mMessenger;
  8. synchronized (sm.mHandler.mLockObject) {
  9. //将CMD_CHANNEL_FULL_CONNECTION发送给远端的服务
  10. dstMessenger.send(msg);
  11. //阻塞监听唤醒
  12. sm.mHandler.mLockObject.wait();
  13. }
  14. } else {
  15. sm.mHandler.mResultMsg = null;
  16. }
  17. } catch (InterruptedException e) {
  18. sm.mHandler.mResultMsg = null;
  19. } catch (RemoteException e) {
  20. sm.mHandler.mResultMsg = null;
  21. }
  22. Message resultMsg = sm.mHandler.mResultMsg;
  23. sm.recycle();
  24. //将结果返回给客户端
  25. return resultMsg;
  26. }
        整个过程的核心在sendMessageSynchronously中处理,该方法中创建了一个SyncMessenger的对象,这是AsyncChannel的内部静态类, 作用就是负责同步传输消息
        那么SyncMessenger是如何同步传输消息的呢?
        其原理就是当向远端的服务发送消息之后,SyncMessenger就进入阻塞状态(也就是停留在wait这里),等待远程服务端的回应,当拿到返回消息之后,将会唤醒SyncMessenger,然后再将结果返回给客户端
        结合代码来看就是,在将消息发送给服务端之前,将当前SyncMessenger的Messenger对象作为replyTo存放在消息包中:
            msg.replyTo = sm.mMessenger;
 
 
        然后将消息发送给服务端:
            dstMessenger.send(msg);
 
 
        然后进入阻塞状态:
            sm.mHandler.mLockObject.wait();
 
 
        经过这个操作,服务端就收到CMD_CHANNEL_FULL_CONNECTION的消息,并且该消息的replyTo对象是SyncMessenger,然后我们来看该SyncMessenger的唤醒过程。
        在前面我们介绍过,当服务端接收到该请求后,如果同意建立双向通道,则需要connect()之后给出OK的回应,也就是:

 
 
  1. @AsyncChannelServiceFull. java
  2. public void handleMessage (Message msg) {
  3. switch (msg.what) {
  4. case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
  5. //先连接上AsyncChannel
  6. sServiceAsyncChannel.connect(AsyncChannelServiceFull. this, mServiceHandler, msg.replyTo);
  7. //然后给出OK的回应
  8. sServiceAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_SUCCESSFUL);
  9. break;
  10. }
  11. }
  12. }
        我们通过上面回应的流程来看如何唤醒SyncMessenger,记得这里最后一个参数是STATUS_SUCCESSFUL:

 
 
  1. @AsyncChannel. java
  2. public void replyToMessage (Message srcMsg, int what, int arg1) {
  3. Message msg = Message.obtain();
  4. msg.what = what;
  5. msg.arg1 = arg1;
  6. replyToMessage(srcMsg, msg);
  7. }
        继续往下走:

 
 
  1. public void replyToMessage(Message srcMsg, Message dstMsg) {
  2. try {
  3. dstMsg.replyTo = mSrcMessenger;
  4. //这里的replyTo记录的就是当初的SyncMessenger的Handler对象
  5. srcMsg.replyTo.send(dstMsg);
  6. } catch (RemoteException e) {
  7. log( "TODO: handle replyToMessage RemoteException" + e);
  8. e.printStackTrace();
  9. }
  10. }
        到这里我们看到,通过replyToMessage方法,把服务端的消息发送给了Message的replyTo对象,这个对象其实就是我们当初给服务端发送CMD_CHANNEL_FULL_CONNECTION消息时放入的SyncMessenger的Handler对象,因此,此时的SyncMessenger将会收到服务端的消息:

 
 
  1. @AsyncChannel.java
  2. private class SyncHandler extends Handler {
  3. public void handleMessage(Message msg) {
  4. mResultMsg = Message.obtain();
  5. mResultMsg.copyFrom(msg);
  6. synchronized(mLockObject) {
  7. //唤醒SyncMessenger
  8. mLockObject.notify();
  9. }
  10. }
  11. }
        在这里,我们终于看到将SyncMessenger唤醒的操作,当该Handler被唤醒时, sendMessageSynchronously()方法就通过wait()方法继续往下执行,然后就将服务端的消息从SyncMessenger的Handler中取出来,最终return给客户端的fullyConnectSync()调用者。
        至此,双向通道的流程结束,服务端不仅连接上了通道,而且客户端拿到了服务端STATUS_SUCCESSFUL的消息。 
        下面用一张图来描述这个过程:
        



五、消息派发的机制


        消息派发的机制其实在上一节的通道建立机制中简单介绍过了,因此接下来的解释就比较容易理解了。


5.1、异步消息派发过程


        我们知道,异步消息的派发是通过AsyncChannel的sendMessage()来完成的:


 
 
  1. @AsyncChannelClient. java
  2. public static void sendMsgToService () {
  3. sClientAsyncChannel.sendMessage(MainActivity.MSG_ASYNC_REQ);
  4. }
        然后来看AsyncChannel的sendMessage方法:

 
 
  1. @AsyncChannel. java
  2. public void sendMessage (int what) {
  3. Message msg = Message.obtain();
  4. msg.what = what;
  5. sendMessage(msg);
  6. }
  7. public void sendMessage(Message msg) {
  8. msg.replyTo = mSrcMessenger;
  9. try {
  10. //向mDstMessenger发送消息
  11. mDstMessenger.send(msg);
  12. } catch (RemoteException e) {
  13. replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
  14. }
  15. }

        这个过程与前面4.2节中双向通道建立过程是相同的,其实就是将消息通过AsyncChannel发送给mDstMessenger,也就是远程服务端。


5.2、同步消息派发过程


        同步消息的处理其实在前面4.3节中快速双向通道建立流程中介绍过,我们来看一下其流程。

        发送同步消息是通过sendMessageSynchronously()方法实现的:

 
 
  1. @AsyncChannelClient. java
  2. public static void sendSyncMsgToService () {
  3. Message replyMsg = sClientAsyncChannel.sendMessageSynchronously(MainActivity.MSG_SYNC_REQ);
  4. String msg = (String) replyMsg.obj;
  5. Log.d(tag, "get service sync reply msg = " + msg);
  6. }
  7. @AsyncChannel. java
  8. public Message sendMessageSynchronously (int what) {
  9. Message msg = Message.obtain();
  10. msg.what = what;
  11. Message resultMsg = sendMessageSynchronously(msg);
  12. return resultMsg;
  13. }
  14. public Message sendMessageSynchronously(Message msg) {
  15. Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
  16. return resultMsg;
  17. }
  18. private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
  19. //创建SyncMessenger对象
  20. SyncMessenger sm = SyncMessenger.obtain();
  21. try {
  22. if (dstMessenger != null && msg != null) {
  23. //将SyncMessenger的Messenger放入消息包中
  24. msg.replyTo = sm.mMessenger;
  25. synchronized (sm.mHandler.mLockObject) {
  26. //将CMD_CHANNEL_FULL_CONNECTION发送给远端的服务
  27. dstMessenger.send(msg);
  28. //阻塞监听唤醒
  29. sm.mHandler.mLockObject.wait();
  30. }
  31. } else {
  32. sm.mHandler.mResultMsg = null;
  33. }
  34. } catch (InterruptedException e) {
  35. sm.mHandler.mResultMsg = null;
  36. } catch (RemoteException e) {
  37. sm.mHandler.mResultMsg = null;
  38. }
  39. Message resultMsg = sm.mHandler.mResultMsg;
  40. sm.recycle();
  41. //将结果返回给客户端
  42. return resultMsg;
  43. }
        以上过程是不是很熟悉?没错,这就是建立快速双向通道中最关键的部分,其原理就是在客户端发送消息后,AsyncChannel内部创建一个SyncMessenger对象,等消息发送给服务端之后,SyncMessenger就进入阻塞状态,当服务端回应消息时,SyncMessenger就解除阻塞,然后将结果返回给客户端。
        这就是同步消息的传输机制。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值