AsyncChannel原理分析以及实操演练

        AsyncChannel原理分析以及实操演练


Android网络框架分析系列文章目录:

Android P适配以太网功能开发指南
Android以太网框架情景分析之启动简介
Android以太网框架情景分析之EthernetServiceImpl和NetworkManagementService交互深入分析
Android以太网框架情景分析之NetworkManagementService和netd交互深入分析二
Android以太网框架情景分析之NetworkManagementService和netd交互深入分析一
Android以太网框架情景分析之NetworkFactory与NetworkAgent深入分析
AsyncChannel原理分析以及实操演练


前言

  本来最近空闲时间在进行着以太网框架情景分析博客的编写,可是在源码分析的过程中发现了一个知识点就是AsyncChannel,本想忽略这个知识点的,可是发现在Android的整个网络框架(以太网,WIFI,数据网络)都有用到它,所以觉得还是抽出一定的时间来专门研究下它,所以才有了今天的文章。

  注意:本篇的分析的源码是以Android 7 msm8953为基础的,其中牵涉到的源码路径主要如下:

frameworks/base/core/java/android/os/Messenger.java
frameworks/base/core/java/android/os/IMessenger.aidl
frameworks/base/core/java/com/android/internal/util/AsyncChannel.java  //注意这是一个工具类
frameworks/base/core/java/android/os/Handler.java


一. AsyncChannel功能概括

  AsyncChannel是什么呢,从英语字面看来是异步通道(其实这么理解好像不好,应该叫做异步/同步通信通道比较好)。它的源码位于Android工程的 frameworks/base/core/java/com/android/internal/util/AsyncChannel.java,其实它是对Handler和Messenger(注意不是Message而是Messenger,对于该类我们后面会分析)的一个扩展,用于单个进程内(包含不同线程间)或两个进程间的通信。在不同的进程间,AsyncChanne实质上使用的是IMessenger通过Binder的形式对消息进行发送和接收,当然,这种方式对单个进程内非remove service同样适用。 在做更多分析以前,我们还是先看下AsyncChannel的类图,让我们对其中涉及的各种关系先初步认识,再深入了解。

在这里插入图片描述

  在前言中我们讲到在Android系统中有很多地方都使用了这个工具类,特别是Android的网络框架中(以太网,WIFI,移动数据网络)和ConnectivityService之间通过AsyncChannel串联了起来。但是由于AsyncChannel属于系统内部源码,第三方三方应用无法直接进行使用,但是我们内置的应用是可以用的,并且它的设计思想也是值得我们学习的。好了,通过前面的文字介绍我想大伙对AsyncChannel有了一个初步了解了,下面我们从三个方面来对其做一个总结。


1.1 AsyncChannel概括

  主要用于实现两个Handler之间的通信和消息传递。


1.2 AsyncChannel的特点

  AsyncChannel的可以归纳总结为如下三个特点:

  • 可以在单进程或不同进程间实现消息传递,通俗点来讲就是可以跨进程通信
  • 支持建立单向通信或双向通信。啥意思,单向通信有点抗战谍战的感觉只能上级联系到下级,而下级却不能联系到上级。至于双向通信就是可以互相沟通
  • AsyncChannel是对Handler,Messenger的一种包装,并没有实现额外的通信方式,即它并没有创造一个IPC或者其它的通信方式,正如前面所讲他就是一个工具类,只是对其它的通信方式的封装

1.3 AsyncChannel工作模式

  这里所说的AsyncChannel工作模式,其实就是单双工两种通信方式:

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


二. Messenger概括

  重要的事情说三篇是Messenger,Messenger,Messenger不是Message。

  如果说AsyncChannel实现了两个Handler之间的通信,那么首要功臣就是Messenger了,可是它是那么低调朴实,让我不得不给大伙介绍一番。Messenger的源码位于 frameworks/base/core/java/android/os/Messenger.java,是对Handler的一个再包装类,至于为什么AsyncChannel能跨进程实现两个Handler之间的通信(并不是AsyncChannel真的开发了另外的IPC通信的机制),其奥秘就在与Messenger结合了Binder机制,使得跨进程间Message的传递和处理成为了可能。

2.1 Messenger的成员变量和构造函数

  Messenger的主要成员变量是IMessenger类型的mTarget,该变量可以通过Handler或者IBinder进行初始化,分别应用于同进程消息或者跨Service(remote或者非remote)消息发送:

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;// IMessenger类型,mTarget代表message的目的端
    
    // 初始化mTarget并指向Handler中的IMessenger
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
	// 初始化mTarget并指向IBinder的实际类型,这是实现跨进程通信的核心
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
}

2.2 Messenger.send发送函数

  无论是否进行跨进程,Messenger实际上都是利用IMessenger进行消息发送的:

    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

2.3 IMessenger

  IMessenger是一个aidl接口,其在Android源码位于 frameworks/base/core/java/android/os/IMessenger.aidl,其目的是通过AIDL使用Binder机制对Message进行发送:

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

  其在源码编译环境下生成的IMessenger.java内容,至于为什么可以实现跨进程通信小伙伴们心里应该有数了吗。

package android.os;

public interface IMessenger extends IInterface {
	void send(final Message p0) throws RemoteException;

	public abstract static class Stub extends Binder implements IMessenger {
		private static final String DESCRIPTOR = "android.os.IMessenger";
		static final int TRANSACTION_send = 1;

		public Stub() {
			this.attachInterface((IInterface) this, "android.os.IMessenger");
		}

		public static IMessenger asInterface(final IBinder obj) {
			if (obj == null) {
				return null;
			}
			final IInterface iin = obj
					.queryLocalInterface("android.os.IMessenger");
			if (iin != null && iin instanceof IMessenger) {
				return (IMessenger) iin;
			}
			return new Proxy(obj);
		}

		public IBinder asBinder() {
			return (IBinder) this;
		}

		public boolean onTransact(final int code, final Parcel data,
				final Parcel reply, final int flags) throws RemoteException {
			switch (code) {
				case 1598968902 : {
					reply.writeString("android.os.IMessenger");
					return true;
				}
				case 1 : {
					data.enforceInterface("android.os.IMessenger");
					Message _arg0;
					if (0 != data.readInt()) {
						_arg0 = (Message) Message.CREATOR
								.createFromParcel(data);
					} else {
						_arg0 = null;
					}
					this.send(_arg0);
					return true;
				}
				default : {
					return super.onTransact(code, data, reply, flags);
				}
			}
		}

		private static class Proxy implements IMessenger {
			private IBinder mRemote;

			Proxy(final IBinder remote) {
				this.mRemote = remote;
			}

			public IBinder asBinder() {
				return this.mRemote;
			}

			public String getInterfaceDescriptor() {
				return "android.os.IMessenger";
			}

			@Override
			public void send(final Message msg) throws RemoteException {
				final Parcel _data = Parcel.obtain();
				try {
					_data.writeInterfaceToken("android.os.IMessenger");
					if (msg != null) {
						_data.writeInt(1);
						msg.writeToParcel(_data, 0);
					} else {
						_data.writeInt(0);
					}
					this.mRemote.transact(1, _data, (Parcel) null, 1);
				} finally {
					_data.recycle();
				}
			}
		}
	}
}

2.4 以Handler为参数初始化Messenger并通信流程

  这个其实并没有什么新意,其实就是对Handler进行了一下封装。Handler中的MessengerImpl对IMessenger进行了实现,而在该情况下 send 方法中中最终还是使用Handler自身对Message进行了处理,因此,这里的IMessenger仅仅是一个中介,且此而已:

    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本身的sendMessage完成了通信
            Handler.this.sendMessage(msg);
        }
    }

2.5 以Service IBinder为参数初始化Messenger并通信流程

  在Android源码中有相关的Messenger的以Service IBinder为参数的演示实例,源码的路径如下所示:

frameworks/base/core/tests/coretests/src/android/os/MessengerService.java 
frameworks/base/core/tests/coretests/src/android/os/MessengerTest.java

  我这边将其独立出来,给大伙演示出来。

2.5.1 Dst Service端实现

  其源码的逻辑如下所示:

public class MessengerService extends Service {
    @SuppressLint("HandlerLeak")
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Message reply = Message.obtain();
            reply.copyFrom(msg);
            try {
                msg.replyTo.send(reply);
            } catch (RemoteException e) {
            }
        }
    };
    
    private final Messenger mMessenger = new Messenger(mHandler);
    
    public MessengerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
    	//设立获取的是Binder代理对象
        return mMessenger.getBinder();
    }
}

//定义在Messenger.java中
 public IBinder onBind(Intent intent) {
     return mMessenger.getBinder();
 }

//定义在IMessenger.java中
public static IMessenger asInterface(final IBinder obj) {
	if (obj == null) {
		return null;
	}
	final IInterface iin = obj
			.queryLocalInterface("android.os.IMessenger");
	if (iin != null && iin instanceof IMessenger) {
		return (IMessenger) iin;
	}
	return new Proxy(obj);
}

  Dst Service在AndroidManifest.xml中的配置如下:

<service android:name="com.itgentalman.asyncchannelfun.MessengerService"
   android:process=":remote"
/>

2.5.2 Src Client的端实现

  该代码定义在MessengerTest.java中,其主要的代码如下所示:

    private Messenger mServiceMessenger;
    private static final String TAG = "MessengerTest";

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (MessengerTest.this) {
            	//通过aidl跨进程调用获取IBinder类型的Service,从而通过IBinder类型的service对Messenger中的mTarget进行初始化
                mServiceMessenger = new Messenger(service);
                MessengerTest.this.notifyAll();
            }
        }

        public void onServiceDisconnected(ComponentName name) {
            mServiceMessenger = null;
        }
    };
    
    protected void setUp(Context context) throws Exception {
        context.bindService(new Intent(context, MessengerService.class),
                mConnection, Context.BIND_AUTO_CREATE);
        synchronized (this) {
            while (mServiceMessenger == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
    }
}

  通过 bindService 与MessengerService建立连接从而获取IBinder类型的Service(注意这个IBinder并不是远程MessengerService的,而是Messenger的),连接完成后,对Messenger进行初始化。

2.5.3 Messenger跨进程原理分析

  我想到这里大伙应该清楚为啥Messenger能实现跨进程的通信了,其实还是用到了aidl的Binder通信方法,经过前面的一番操作后,MessengerTest.mServiceMessenger.mTarget持有了MessengerService.mMessenger.mTarget远程Binder代理,所以当我们实际操作客户端的Binder mTarget代理端就相当于操作服务端的mTarget了,而服务端的mTarget又关联上了服务端的mHandler,所以就实现了跨进程Handler通信。

在这里插入图片描述

  是不是有点开窍了感觉,再理一理!如果还是木有理解,老衲也无能为力了。

  • 实质上Messenger跨进程通信,最后是通过IMessenger利用Binder机制进行消息的发送。
  • 我们知道Handler中的MessengerImpl实现了IMessenger.aidl中的 send方法,并在其中调用Handler自身的 sendMessage 函数进行消息处理
  • Service中可以定义自己的Handler和对应的Messenger,并在onBind时返回Handler中MessengerImpl的实例,即IMessenger.stub的实例,而在Client端在 onServiceConnected方法中通过aidl跨进程调用获取其Binder代理对象IBinder Proxy对象,然后在Client中以该IBinder对象做为参数对Messenger进行初始化,之后,Client端就可以通过该Messenger将消息发送到Service中的Handler进行处理了。Client和Service间通过Messenger进行消息传递的大致流程如下图:

在这里插入图片描述



三 AsyncChannel类简要分析

  刀已经磨好了,前期知识也准备的OK了,得正式开干了。对于一个人基本是从脸开始的不是,而想了解一个类一般都是从成员变量和成员方法开始,好吗,对于AsyncChannel我们也不例外。


3.1 AsyncChannel成员常量和变量

  在正式开始前,或者说在开始整个篇章前有一个规则必须先制定,那就是我们称连接的首次发起端为source端(简称Src端),被连接端为destination端(简称Dst端),这个很重要,不然在接下来的分析中你一定会over的。

3.1.1 AsyncChannel连接指令相关静态常量

/** AsyncChannel消息码在系统中的唯一开始标志 */
private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL;

/** 单向连接建立后,AsyncChannel通知source端半连接已建立,可以进行单向通信,
    但此时destination端是完全没有感知的 */
public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;

/** source端在半连接建立后,发送该消息码给destination端,请求建立双向连接 */
public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;

/** destination端在建立双向连接后,发送该消息码给source端,告知双向连接建立完成 */
public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;

/** source或者destination主动请求断开 */
public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;

/** 在一端主动断开后,对端会收到该消息码 */
public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;

3.1.2 AsyncChannel连接和消息状态相关静态常量

/** 连接状态相关:成功半连接,成功连接,成功断开 */
public static final int STATUS_SUCCESSFUL = 0;

/** 跨service连接,bindService成功或者失败 */
public static final int STATUS_BINDING_UNSUCCESSFUL = 1;

/** 消息发送失败 */
public static final int STATUS_SEND_UNSUCCESSFUL = 2;

/** 已建立双向连接,拒绝再次连接 */
public static final int STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3;

/** 跨service连接,对端service死亡,binderDied返回后发送该状态码 */
public static final int STATUS_REMOTE_DISCONNECTION = 4;

3.1.3 AsyncChannel主要成员变量

/** ServiceConnection,用于和service或remote service建立连接 */
private AsyncChannelConnection mConnection;

/** 连接建立端Context,仅用于bind/unbind service时使用 */
private Context mSrcContext;

/** binderDied回调监听,仅用于与service连接后使用 */
private DeathMonitor mDeathMonitor;

/** 连接建立端Handler */
private Handler mSrcHandler;

/** 连接建立端Messenger */
private Messenger mSrcMessenger;

/** 连接对端Messenger */
private Messenger mDstMessenger;

3.2 AsyncChannel主要方法

  这里只例举出基本方法,暂时不做过多详解,会带入实际案例进行详解。

connect();//以及各种重载,主要是用来连接的
disconnect();//断开连接
sendMessage();//发送消息
replyToMessage();//回复消息
sendMessageSynchronously();//同步发送消息
fullyConnectSync();//建立快速全连接方法


四 AsyncChannel各种使用场景实操演练测试源码架构分析

  本来想一开始就讲解单向通道建立的原理,但是一想这个有点反人类,一般的过程都是先用然后再原理,好吗,我是个俗人也不能免俗。还是先实操再分析!

  记得我们在前面讲述过AsyncChannel是一个内部类,没有对外公开,至于怎么使用这个可以在ROM环境中使用或者将源码环境下编译生成的的framework.jar包导入,亦或者是引入layoutlib.jar总有一个适合你。

  为了将AsyncChannel各种通信模型搞懂,并且也为了将博客透彻我本人亲自操作设计了AsyncChannel实操演练的代码架构,比较简单,总的框架图如下:

在这里插入图片描述

  • MainActivity.java
      它就是我们测试程序的脸面了颜值担当了,主要是界面的显示和实例的触发,其界面如下:
    在这里插入图片描述

  • AsyncChannelDstService.java
      作为目的端(Dst端)或者说是服务端,接受Src端或者说是客户端请求

  • AsyncChannelRemoteDstService.java
      作为跨进程目的端(Dst端)或者说是服务端,接受Src端或者说是客户端请求

  • AsyncChannelSrcService.java
      作为Src端或者说客户端,主要用于向Dst端或者说是服务端发送请求

  • AsyncConstant.java
      没有说好说的,存放的一些测试中用到的变量定义

  • MessengerService.java MessengerTest.java SyncHandlerThread.java
      用于演示Messenger跨进程调用的测试代码



五 AsyncChannel通道使用实操演练以及原理分析


5.1 AsyncChannel单向通道(Half Connect)实操演练

  在前面的章节我们介绍了AsyncChannel实操演练的代码架构,而在这里我们只需要轻轻点击单向通道测试按钮即可,就会依次触发启动服务端和客户端,当客户端的Server启动(onStartCommand)时,就会与服务端建立单项通道。下面我们来依次来看看代码:

5.1.1 点击触发动作处理代码

  这里只截取主要代码,整个工程可以见博客结尾的链接地址。

	//MainActivity.java
    private Button bt1;// 单向通道测试
    private Button bt2;// 在单向通道基础上建立双向通道
    private Button bt3;//快速双向通道建立
    private Button bt4;//异步消息发送
    private Button bt5;//同步消息发送
    
    private Button bt6;//跨进程通信

    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.bt1://单项通道测试
            do_singal_half_connect();
            break;
    }

    // 单向通道测试
    private void do_singal_half_connect() {
        // 启动Dst端
        Intent mAsyncDst = new Intent();
        mAsyncDst.setComponent(new ComponentName(MainActivity.this,
                AsyncChannelDstService.class));

        startService(mAsyncDst);

        // 启动Src端
        Intent mAsyncSrc = new Intent();
        mAsyncSrc.setComponent(new ComponentName(MainActivity.this,
                AsyncChannelSrcService.class));
        mAsyncSrc.putExtra("mode", AsyncConstant.SINGAL_HALF_CONNECT);
        startService(mAsyncSrc);
    }

5.1.2 客户端初始化流程代码

	//AsyncChannelSrcService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        async_mode = intent.getExtras().getInt("mode");
        Log.d(TAG, "async_mode : " + async_mode);
        if (async_mode == AsyncConstant.SINGAL_HALF_CONNECT) {// 单向通道通信
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            mAsyncChannelSrc.connect(AsyncChannelSrcService.this, mHandlerSrc,
                    AsyncChannelDstService.getMessage());
        } else if (async_mode == AsyncConstant.MULTI_FULL_CONNECT) {// 在单向通道基础上建立双向通道
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            mAsyncChannelSrc.connect(AsyncChannelSrcService.this, mHandlerSrc,
                    AsyncChannelDstService.getMessage());
        } else if (async_mode == AsyncConstant.FAST_MULTI_FULL_CONNECT) {//快速双向通道建立
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                mAsyncChannelSrc.sendMessage(AsyncConstant.ASYNC_TEST);
            }
        }else if(async_mode == AsyncConstant.SEND_ASYNC_MESSAGE){//发送异步消息
            //1.先建立快速双向通道连接
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());
            //2.连接成功以后,发送异步消息
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                mAsyncChannelSrc.sendMessage(AsyncConstant.ASYNC_MSG_REQ);
            }
        }else if(async_mode == AsyncConstant.SEND_SYNC_MESSAGE){//发送同步消息
            //1.先建立快速双向通道连接
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());
            //2.连接成功以后,发送异步消息
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                Message  reply = mAsyncChannelSrc.sendMessageSynchronously(AsyncConstant.SYNC_MSG_REQ);
                Log.d(TAG,"reply msg : " + reply.toString());
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

上面的过程中,通过服务端的getServiceMessenger()方法拿到服务端的Messenger对象,其过程是:

	//AsyncChannelDstService.java
    public static Messenger getMessage() {
        if (mHandlerDst == null) {
            mHandlerThreadDst = new HandlerThread("syncDst");
            mHandlerThreadDst.start();
            mHandlerDst = new HandlerDst(mHandlerThreadDst.getLooper());
            return new Messenger(mHandlerDst);
        }
        return new Messenger(mHandlerDst);
    }

5.1.3 单项通道测试结果演示

  当如上一番猛虎般的操作完成以后,我们客户端发送完connect()请求之后,客户端需要做的就是在Handler对象中等待Message消息,主要逻辑如下:

		//AsyncChannelSrcService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Dst : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                Log.d(TAG, "CMD_CHANNEL_HALF_CONNECTED");
                break;

            default:
                break;
            }
            super.handleMessage(msg);
        }

  我们运行可以查看到如下的打印信息:

# logcat  -s ASYNC/Src
--------- beginning of main
--------- beginning of system
07-20 10:36:45.087  8707  8707 D ASYNC/Src: onStartCommand
07-20 10:36:45.087  8707  8707 D ASYNC/Src: async_mode : 0
07-20 10:36:45.093  8707  8739 D ASYNC/Src: handleMessage from Dst : 69632
07-20 10:36:45.093  8707  8739 D ASYNC/Src: CMD_CHANNEL_HALF_CONNECTED

  这说明客户端接收到了AsyncChannel.CMD_CHANNEL_HALF_CONNECTED消息,当接收到该信息后说明当前的单项通道建立成功。此时我们的客户端可以通过sAsyncChannel的sendMessage()方法向服务端发送消息了。而后面的双向通道的建立也是通过sendMessage来进行的。

5.1.4 单项通道测试小结

  如上就是就是简单的单项的测试的整个过程了了,是不是有点像C/S架构,客户请求服务端。如果我们想将这种架构运用于实战其实很简单,作为客户端,如果想要创建与服务端之间的AsyncChannel,需要做以下几个准备:

  • 获取服务端的Messenger对象,该对象其实就是利用服务端的Handler构建的Messenger;
  • 创建客户端自己的Handler对象;
  • 创建AsyncChannel对象;
  • 通过AsyncChannel对象,连接当前的Handler和服务端的Messenger,然后通过connect发起申请连接。

5.2 AsyncChannel单向通道(Half Connect)原理揭秘

  通过前面的3.3章节我想小伙伴对于AsyncChannel单向通道的运用应该已经轻车熟路了,如果想构建AsyncChannel单向通道只需简简单单的四个步骤即可:

  • 获取服务端的Messenger对象,该对象其实就是利用服务端的Handler构建的Messenger;
  • 创建客户端自己的Handler对象;
  • 创建AsyncChannel对象;
  • 通过AsyncChannel对象,连接当前的Handler和服务端的Messenger,然后通过connect发起申请连接。

  好吗,前面的三个步骤都没有啥特殊的,看来实现AsyncChannel单向通道的奥秘在第四个步骤connect中,让我们来分析分析其原理。

5.2.1 connect发起单向通道连接请求

	//AsyncChannel.java
    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
        

        // 主要用于客户端AsyncChannel相关成员变量的初始化,见章节5.2.2
        connected(srcContext, srcHandler, dstMessenger);

        //发送"CMD_CHANNEL_HALF_CONNECTED"告知source端半连接已建立
        replyHalfConnected(STATUS_SUCCESSFUL);//见章节5.2.3

        
    }

5.2.2 connected

	//AsyncChannel.java
	//根据掺入的参数,初始化Src(客户端的)成员变量
    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
        

        // Initialize source fields
        mSrcContext = srcContext;
        mSrcHandler = srcHandler;
        mSrcMessenger = new Messenger(mSrcHandler);

        
        mDstMessenger = dstMessenger;
        linkToDeathMonitor();
        
    }

  这个方法逻辑比较简单,整体的逻辑可以概括如下:

  • 主要是初始化AsyncChannel的成员变量
  • 结合我们前面的实例,在初始化AsyncChannel时,传递了本地的Handler对象和目标的Messenger对象,在这里用本地的Handler对象创建Messenger对象mSrcMessenger ,然后将目标端的Messenger对象保存在成员变量mDstMessenger 中

5.2.3 replyHalfConnected

	//AsyncChannel.java
    private void replyHalfConnected(int status) {
        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
        msg.arg1 = status;
        msg.obj = this;
        msg.replyTo = mDstMessenger;
        if (!linkToDeathMonitor()) {
            // Override status to indicate failure
            msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
        }
		//发送消息
        mSrcHandler.sendMessage(msg);
    }

  这个方法言简意赅,就干了一件事件就是向客户端发送了一个消息CMD_CHANNEL_HALF_CONNECTED,通知客户端单向通道已经OK了。至此单向通道建立的connect函数就分析完成了,我想此刻的你一定有句话很想说就是这个单向连接搞这么复杂最后其实就是调用客户端的Handler给自己发了一个消息吗,这个不整这套也能做到啊!答案是肯定的,那谷歌为啥多此一举整这么一套呢,谷歌这么做是有道理的主要是为了后续的双向通道的建立做准的。

5.2.4 AsyncChannel单向通道(Half Connect)总结

  现在再来让我们复盘一下该过程,整个半连接过程由连接发起客户端(Src端)完成,服务端(Dst目的端)没有任何消息告知。

  • 客户端(Src端)调用AsyncChannel的 connect 函数后,开始对AsyncChannel中变量的初始化,主要是对mDstMessenger的初始化。完成后客户端(Src端)Handler会收到 “CMD_CHANNEL_HALF_CONNECTED” 的消息告知半连接已经完成,
  • 之后,客户端(Src端)就可以通过该AsyncChannel与服务端(Dst目的端)进行通信。这种通信方式是单向的,只能由客户端(Src端)主动向服务端(Dst目的端)推送消息并获取其回复,服务端(Dst目的端)无法主动向客户端(Src端)送消息。这个连接过程可以由下面的时序图来表示:

在这里插入图片描述


5.3 AsyncChannel在单向通道(Half Connect)基础上建立双向通道实操演练

  前面的你一定还在纳闷搞这么复杂的流程才建立了一个单向通道,是不是有点然并卵的感觉,这不我们可以在此基础上通过由客户端(Src端)在接受到CMD_CHANNEL_HALF_CONNECTED指令后可以通过发送 “CMD_CHANNEL_FULL_CONNECTION” 请求从而建立双向全连接。

5.3.1 服务(Dst)端的初始化流程

  这里用户点击触发的启动客户端和服务端的流程基本一致,只是传入的参数有点差别而已,就不贴出来了,大家可以查看下整个工程就可以知道,这里重点介绍服务(Dst)端的处理。首先,服务端本身需要初始化Handler,并且如果服务端准备提供双向通道,那么就需要创建自己的AsyncChannel对象(即客户端和服务端都拥有对方Messenger的引用这个才是关键)。而这些工作通常需要在服务端初始化时完成:

	//AsyncChannelDstService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        mAsyncChannelDst = new AsyncChannel();//初始化AsyncChannel
        mHandlerThreadDst = new HandlerThread("syncDst");
        mHandlerThreadDst.start();
        mHandlerDst = new HandlerDst(mHandlerThreadDst.getLooper());
        mContext = this;
        return super.onStartCommand(intent, flags, startId);
    }

5.3.2 客户(Src)端的处理流程

  客户端前面处理的流程和单向通道建立是一致的,只有当客户端与服务端的单项通道创建完成后,也就是当客户端收到AsyncChannel.CMD_CHANNEL_HALF_CONNECTED之后,我们可以向AsyncChannel对象发送CMD_CHANNEL_FULL_CONNECTION消息申请建立双向通道。也就是在刚才接收到的消息基础上,发送CMD_CHANNEL_FULL_CONNECTION请求即可,这种通信机制在Android的网络框架中运用比较多。特别是在网络注册到ConnectivityService中。

		//AsyncChannelSrcService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Dst : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED://是不是有种似曾相识的感觉
                Log.d(TAG, "CMD_CHANNEL_HALF_CONNECTED");
                // Src客户端单项通道建立完成,继续申请双向通道
                mAsyncChannelSrc
                        .sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);

                break;
			}
	}

5.3.3 服务(Dst)端的处理流程

  此时的服务端将会在handleMessage中接收来自客户端的消息,然后并响应之:

		//AsyncChannelDstService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                // 服务端接收到双向通道的建立请求
                // 如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
                mAsyncChannelDst.connect(mContext, mHandlerDst, msg.replyTo);
                Log.d(TAG, "CMD_CHANNEL_FULL_CONNECTION");
                // 全连接已经建立,发送 "CMD_CHANNEL_FULLY_CONNECTED" 给AsyncChannelSrcService或者是SyncMessenger,这个根据情况而定
                mAsyncChannelDst.replyToMessage(msg,
                        AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                        AsyncChannel.STATUS_SUCCESSFUL);

                break;
          	}
         }

  从上面的代码我们可以看到,当客户端发起双向通道建立的申请后,服务端将会收到AysncChannel.CMD_CHANNEL_FULL_CONNECTION的请求,此时的服务端如果同意建立双向通道,则使用自己的AsyncChannel对象将自己的Handler与客户端的Messenger连接起来。至此,双向通道建立完成,此时不仅客户端可以通过自己的AsyncChannel对象向服务端发送请求,服务端也可以通过自己的AsyncChannel对象向客户端发送请求。

5.3.4 双向通道全连接测试结果演示

  经过上述一番猛如虎的操作之后,将会在客户端接收到服务端的发送的CMD_CHANNEL_FULLY_CONNECTED信息,如果接受到则表明全连接建立成功,逻辑如下:

		//AsyncChannelSrcService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Dst : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                Log.d(TAG, "CMD_CHANNEL_FULLY_CONNECTED");
                mAsyncChannelSrc.sendMessage(AsyncConstant.ASYNC_TEST);//验证是否可以双向通信了
                break;
           }
     	}

打印信息如下:

在这里插入图片描述

5.3.5 双向通道全连接测试小结

  朋友们你们都学会了没有啊,下面我们来小结一下在单向半连接基础上建立双通道全连接的要注意的点:

  • 前提当然是得建立单向通道半连接了
  • 服务端也必须建立自己的AsyncChannel,然后将自己的Handler和客户端传递过来的Messenger关联起来,通过connect发起双向连接
  • 经过如上操作之后双向通道全连接就OK了

5.4 AsyncChannel在单向通道(Half Connect)基础上建立双向通道原理揭秘

  单向通道的建立我们就不需要分析了,因为我们已经掌握OK了,我们从客户端收到CMD_CHANNEL_HALF_CONNECTED回复然后发送CMD_CHANNEL_FULL_CONNECTION地方入手,逻辑代码如下:

		//AsyncChannelSrcService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Dst : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED://是不是有种似曾相识的感觉
                Log.d(TAG, "CMD_CHANNEL_HALF_CONNECTED");
                // Src客户端单项通道建立完成,继续申请双向通道
                mAsyncChannelSrc
                        .sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);

                break;
			}
	}

5.4.1 AsyncChannel.sendMessage

	//AsyncChannel.java
    public void sendMessage(int what) {
        Message msg = Message.obtain();
        msg.what = what;
        sendMessage(msg);
    }
    public void sendMessage(Message msg) {
        msg.replyTo = mSrcMessenger;
        try {
            mDstMessenger.send(msg);
        } catch (RemoteException e) {
            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
        }
    }

  这里的sendMessage只做了一件事情就是将客户端发送的CMD_CHANNEL_FULL_CONNECTION消息封装在Message中,然后通过mDstMessenger的send方法发送给服务端,而在前面我们知道Messenger的send方法最终调用的还是Message的send方法,如下所示:

	//Messenger.java
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

5.4.2 AsyncChannel.replyToMessage

  当客户端将CMD_CHANNEL_FULL_CONNECTION消息发送出来以后,服务端将收到客户端发送过来的消息,然后调用connect和客户建立双向连接并调用replyToMessage将消息反馈会客户端:

		//AsyncChannelDstService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                // 服务端接收到双向通道的建立请求
                // 如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
                mAsyncChannelDst.connect(mContext, mHandlerDst, msg.replyTo);
                Log.d(TAG, "CMD_CHANNEL_FULL_CONNECTION");
                // 全连接已经建立,发送 "CMD_CHANNEL_FULLY_CONNECTED" 给AsyncChannelSrcService或者是SyncMessenger,这个根据情况而定
                mAsyncChannelDst.replyToMessage(msg,
                        AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                        AsyncChannel.STATUS_SUCCESSFUL);

                break;
              }
         }

  这里可以看到当服务端收到CMD_CHANNEL_FULL_CONNECTION的请求后会调用connect向客户端发起申请连接请求,这个和前面单向连接的流程是一致的就不分析了即初始化服务端AsyncChannel相关的成员变量,然后向服务端自己发送CMD_CHANNEL_HALF_CONNECTED的消息,我们主要看看这里的replyToMessage方法,其代码如下:

	//AsyncChannel.java
    public void replyToMessage(Message srcMsg, int what) {
        Message msg = Message.obtain();
        msg.what = what;
        replyToMessage(srcMsg, msg);
    }
    public void replyToMessage(Message srcMsg, Message dstMsg) {
        try {
            dstMsg.replyTo = mSrcMessenger;
            srcMsg.replyTo.send(dstMsg);//这里的replyTo即客户端的Messenger,最好会调用到Message的send
        } catch (RemoteException e) {
            log("TODO: handle replyToMessage RemoteException" + e);
            e.printStackTrace();
        }
    }

5.4.3 AsyncChannel在单向通道(Half Connect)基础上建立双向通道原理揭秘小结

  如上就是整个过程的分析,从整个过程来看从这个过程来看,其实双向通道的建立过程就是在客户端和服务端分别初始化AsyncChannel成员变量的过程,然后通过相互持有对方的Messenger进行Handler通信而已,木有很多神秘的感觉。整个的时序图如下所示。

在这里插入图片描述


5.5 AsyncChannel快速建立双向通道全连接实操演练

  各位小伙伴们是不是有一个感觉,在单向通道基础上建立双向通道全连接有点麻烦了,必须经过下面的步骤才能OK,感觉有点麻烦啊!

  • 前提当然是得建立单向通道半连接了
  • 服务端也必须建立自己的AsyncChannel,然后将自己的Handler和客户端传递过来的Messenger关联起来,通过connect发起双向连接,然后服务端必须还向客户端发送一次消息确认,客户端才知道是否已经正确连接
  • 经过如上操作之后双向通道全连接就OK了

  难道没有一种更加快速,简便的方法吗!当然有了,不然也不会有这个章节了,这里就得我们的fullyConnectSync方法上场了,等了五千年终于轮到我登场了。通过该方法,客户端只需要一次就可以申请到双向通道,并且可以知道通道是否建立成功。下面我们通过俺精心准备的实例演示一番。

5.5.1 服务(Dst)端的初始化流程

  这里用户点击触发的启动客户端和服务端的流程基本一致,只是传入的参数有点差别而已,就不贴出来了,大家可以查看下整个工程就可以知道,这里重点介绍服务(Dst)端的处理。首先,服务端本身需要初始化Handler,并且如果服务端准备提供双向通道,那么就需要创建自己的AsyncChannel对象(即客户端和服务端都拥有对方Messenger的引用这个才是关键)。而这些工作通常需要在服务端初始化时完成:

	//AsyncChannelDstService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        mAsyncChannelDst = new AsyncChannel();
        mHandlerThreadDst = new HandlerThread("syncDst");
        mHandlerThreadDst.start();
        mHandlerDst = new HandlerDst(mHandlerThreadDst.getLooper());
        mContext = this;
        return super.onStartCommand(intent, flags, startId);
    }

5.5.2 客户(Src)端的初始化流程

  此时的服务端已经初始化OK了,那么接下来我们看看客户端要怎么进行相关的初始化工作,其核心就是调用AsyncChannel.fullyConnectSync的方法了,如下所示:

	//AsyncChannelSrcService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        async_mode = intent.getExtras().getInt("mode");
        Log.d(TAG, "async_mode : " + async_mode);
        if (async_mode == AsyncConstant.SINGAL_HALF_CONNECT) {// 单向通道通信
			......
        } else if (async_mode == AsyncConstant.MULTI_FULL_CONNECT) {// 在单向通道基础上建立双向通道
			......
        } else if (async_mode == AsyncConstant.FAST_MULTI_FULL_CONNECT) {//快速双向通道建立
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());//此处是关键
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                mAsyncChannelSrc.sendMessage(AsyncConstant.ASYNC_TEST);
            }
        }else if(async_mode == AsyncConstant.SEND_ASYNC_MESSAGE){//发送异步消息
			......
        }else if(async_mode == AsyncConstant.SEND_SYNC_MESSAGE){//发送同步消息
			......
        }
        return super.onStartCommand(intent, flags, startId);
    }

上面的过程中,通过服务端的getHandler()方法拿到服务端的Handler对象,其过程是:

	//AsyncChannelDstService.java
    public static Handler getHandler() {
        Log.d(TAG, "getHandler");
        if (mHandlerDst == null) {
            Log.d(TAG, "mHandlerDst == null");
            mHandlerThreadDst = new HandlerThread("syncDst");
            mHandlerThreadDst.start();
            mHandlerDst = new HandlerDst(mHandlerThreadDst.getLooper());
        }
        return mHandlerDst;
    }

是不是咋一看和建立单通道半连接的流程有点像啊,是有那么一点相似但是还是有些不同之处:

  • 客户端不再使用connect()方法连接服务端,而是使用fullyConnectSync()方法建立双向通道;
  • 调用fullyConnectSync()建立通道时,使用的是服务端的Handler对象(serviceHandler)而不是Messenger对象;
  • 调用fullyConnectSync()建立通道时,此方法有个返回值,可以明确知道通道是否建立成功;

5.5.3 服务(Dst)端的处理流程

  此时客户端已经将建立快速双向通道全连接的请求发送给了服务端了,服务端就可以收到CMD_CHANNEL_FULL_CONNECTION的消息,然后服务端会处理的方法将会和单向通道基础上建立双向通道逻辑类似,服务端通过AsyncChannel连接上客户端之后回复成功的命令即可完成通道的建立:

		//AsyncChannelDstService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                // 服务端接收到双向通道的建立请求
                // 如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
                mAsyncChannelDst.connect(mContext, mHandlerDst, msg.replyTo);
                Log.d(TAG, "CMD_CHANNEL_FULL_CONNECTION");
                // 全连接已经建立,发送 "CMD_CHANNEL_FULLY_CONNECTED" 给AsyncChannelSrcService或者是SyncMessenger,这个根据情况而定
                mAsyncChannelDst.replyToMessage(msg,
                        AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                        AsyncChannel.STATUS_SUCCESSFUL);

                break;
              }
         }

5.5.4 双向通道全连接测试结果演示

  又是经过上述一番猛如虎的操作之后,将会在客户端接收到服务端的发送的CMD_CHANNEL_FULLY_CONNECTED成功的消息信息,则表明双向连接建立成功了,打印信息如下:

在这里插入图片描述
  通过上述的打印信息,也印证了我们的快速双向通道已经建立OK了。站在上帝的视角出发,该过程中确实比单向通道基础上建立双向通道要简单方便,代码也更加的明了,最主要的是省略了客户端CMD_CHANNEL_HALF_CONNECTED的请求过程,而且客户端可以知道通道的建立结果。我想实战中必选这种方法了,而且这也是官方推荐的做法。 真香!


5.6 AsyncChannel快速建立双向通道全连接原理揭秘

  实战捣鼓完了,又到了我们的原理揭秘时间了,这里的核心方法就是fullyConnectSync,让我们来抽丝剥茧看看它究竟干了些啥!

5.6.1 AsyncChannel.fullyConnectSync

	//AsyncChannel.java
    public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
    	//只是调用connected函数,初始化客户端AsyncChannel的成员变量
        int status = connectSync(srcContext, srcHandler, dstHandler);//见章节5.6.2
        if (status == STATUS_SUCCESSFUL) {
        	//发送CMD_CHANNEL_FULL_CONNECTION给服务(Dst)。并且阻塞等待服务端的返回
            Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);//见章节5.6.3
            status = response.arg1;
        }
        return status;
    }

  这个方法主要调用了两个方法,connectSync和sendMessageSynchronously,让我们来一一跟进,逐个破解其奥秘。

5.6.2 AsyncChannel.connectSync

	//AsyncChannel.java
    public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
        return connectSync(srcContext, srcHandler, new Messenger(dstHandler));
    }
    public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
        if (DBG) log("halfConnectSync srcHandler to the dstMessenger  E");

        // We are connected
        connected(srcContext, srcHandler, dstMessenger);

        if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
        return STATUS_SUCCESSFUL;
    }

  是不是有种似曾相识的感觉,和单向连接过程中调用connect方法然后内部调用connected何其相似,唯一不同的就是没有发送CMD_CHANNEL_HALF_CONNECTED消息返回给客户端。

5.6.3 AsyncChannel.sendMessageSynchronously

  好吗,前面的都好像没有啥特别的就是通过connected初始化了AsyncChannel的成员变量,那看来核心奥秘都在sendMessageSynchronously了,让我们来见识见识其庐山真面目。

	//AsyncChannel.java
    public Message sendMessageSynchronously(int what) {//此处的参数是CMD_CHANNEL_FULL_CONNECTION
        Message msg = Message.obtain();
        msg.what = what;
        Message resultMsg = sendMessageSynchronously(msg);
        return resultMsg;
    }
    
    public Message sendMessageSynchronously(Message msg) {
        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);//详见5.6.4
        return resultMsg;
    }

尼玛怎么又来了一个SyncMessenger.sendMessageSynchronously,干程序员就是苦,剥了一层又是一层,都不能直接点的。继续剥!

5.6.4 SyncMessenger.sendMessageSynchronously

  这里我们先来关注一下SyncMessenger,发现它是AsyncChannel的内部类,其逻辑代码如下所示:

 /**
     * Helper class to send messages synchronously
     */
    private static class SyncMessenger {
        /** A stack of SyncMessengers */
        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
        /** A number of SyncMessengers created */
        private static int sCount = 0;
        /** The handler thread */
        private HandlerThread mHandlerThread;
        /** The handler that will receive the result */
        private SyncHandler mHandler;
        /** The messenger used to send the message */
        private Messenger mMessenger;

        /** private constructor */
        private SyncMessenger() {
        }

        /** Synchronous Handler class */
        private class SyncHandler extends Handler {
            /** The object used to wait/notify */
            private Object mLockObject = new Object();
            /** The resulting message */
            private Message mResultMsg;

            /** Constructor */
            private SyncHandler(Looper looper) {
                super(looper);
            }
            /** Handle of the reply message */
            @Override
            public void handleMessage(Message msg) {
                mResultMsg = Message.obtain();
                mResultMsg.copyFrom(msg);
                synchronized(mLockObject) {
                    mLockObject.notify();
                }
            }
        }

        /**
         * @return the SyncMessenger
         */
        private static SyncMessenger obtain() {
            SyncMessenger sm;
            synchronized (sStack) {
                if (sStack.isEmpty()) {
                    sm = new SyncMessenger();
                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
                    sm.mHandlerThread.start();
                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
                    sm.mMessenger = new Messenger(sm.mHandler);
                } else {
                    sm = sStack.pop();
                }
            }
            return sm;
        }
        /**
         * Recycle this object
         */
        private void recycle() {
            synchronized (sStack) {
                sStack.push(this);
            }
        }

        /**
         * Send a message synchronously.
         *
         * @param msg to send
         * @return result message or null if an error occurs
         */
        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
            SyncMessenger sm = SyncMessenger.obtain();//创建SyncMessenger对象sm
            try {
                if (dstMessenger != null && msg != null) {
                    msg.replyTo = sm.mMessenger;//将前面创建的sm来初始化Message对象msg
                     // 获取对象锁:"sm.mHandler.mLockObject"
                    synchronized (sm.mHandler.mLockObject) {
                        dstMessenger.send(msg);//将组装好的msg信息发送给服务端,此时的what的值是CMD_CHANNEL_FULL_CONNECTION
                        sm.mHandler.mLockObject.wait();//此处是关键,阻塞等待被唤醒
                    }
                } else {
                    sm.mHandler.mResultMsg = null;
                }
            } catch (InterruptedException e) {
                sm.mHandler.mResultMsg = null;
            } catch (RemoteException e) {
                sm.mHandler.mResultMsg = null;
            }
            //被唤醒,消息回复已经收到,返回该回复
            Message resultMsg = sm.mHandler.mResultMsg;
            sm.recycle();
            return resultMsg;
        }
    }

  这个类的内容不是很多,核心的方法就是我们的sendMessageSynchronously颜值担当了,该方法中会获取一个SyncMessenger的对象,这是AsyncChannel的内部静态类,作用就是负责同步传输消息。那么这里核心的一点就是怎么做到同步信息传输呢?这里运用到了LockObject锁机制,在向服务端传输信息之后,SyncMessenger就进入阻塞状态(也就是停留在wait这里),等待远程服务端的回应,当拿到返回消息之后,将会唤醒SyncMessenger,然后再将结果返回给客户端。

是不是还是有点迷糊,我们再来捋一捋流程和核心点:

  • 先创建SyncMessenger对象sm
  • 然后将创建的sm对象指向msg.replyTo,这个点很重要
msg.replyTo = sm.mMessenger;
  • 然后将上述封装好的Message信息发送给服务端
dstMessenger.send(msg);
  • 消息发送完之后,整个流程将进入休眠阻塞状态,等待服务端的唤醒
sm.mHandler.mLockObject.wait();

  在这里通过SyncMessenger向对端发送完消息后,对端如果已经完成了 connect 的操作或者拒绝连接,都应该回复消息。而我们的服务端在收到客户端发送的消息之后做出了如下的操作:

        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                // 服务端接收到双向通道的建立请求
                // 如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
                mAsyncChannelDst.connect(mContext, mHandlerDst, msg.replyTo);
                Log.d(TAG, "CMD_CHANNEL_FULL_CONNECTION");
                // 全连接已经建立,发送 "CMD_CHANNEL_FULLY_CONNECTED" 给SyncMessenger 
                mAsyncChannelDst.replyToMessage(msg,
                        AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                        AsyncChannel.STATUS_SUCCESSFUL);
            }
       	}

很明显的看到我们的服务端同意了请求,然后调用replyToMessage发送发送 “CMD_CHANNEL_FULLY_CONNECTED” 和STATUS_SUCCESSFUL给请求者,看看replyToMessage做了什么操作:

	//AsyncChannel.java
    public void replyToMessage(Message srcMsg, int what) {
        Message msg = Message.obtain();
        msg.what = what;
        replyToMessage(srcMsg, msg);
    }

    public void replyToMessage(Message srcMsg, Message dstMsg) {
        try {
            dstMsg.replyTo = mSrcMessenger;
            srcMsg.replyTo.send(dstMsg);
        } catch (RemoteException e) {
            log("TODO: handle replyToMessage RemoteException" + e);
            e.printStackTrace();
        }
    }

这里重点来了,由于发送消息前设定了msg.replyTo = sm.mMessenger,因此回复的消息会被SyncManager中的SyncHandler处理:

		//AsyncChannel.java
        private class SyncHandler extends Handler {
            private Object mLockObject = new Object();
            private Message mResultMsg;

            private SyncHandler(Looper looper) {
                super(looper);
            }

            @Override
            public void handleMessage(Message msg) {
                mResultMsg = Message.obtain();
                mResultMsg.copyFrom(msg);//将msg信息放到mResultMsg中
                synchronized(mLockObject) {
                    mLockObject.notify();//唤醒客户端
                }
            }
        }

  SyncMessenger被唤醒之后将服务端返回的Message信息从SyncHandler对象中取出来返回给客户端,然后最终返回给fullyConnectSync的调用者,即我们的AsyncChannelSrcService。

        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
            SyncMessenger sm = SyncMessenger.obtain();
            try {
                if (dstMessenger != null && msg != null) {
                    msg.replyTo = sm.mMessenger;
                    synchronized (sm.mHandler.mLockObject) {
                        dstMessenger.send(msg);
                        sm.mHandler.mLockObject.wait();
                    }
                } else {
                    sm.mHandler.mResultMsg = null;
                }
            } catch (InterruptedException e) {
                sm.mHandler.mResultMsg = null;
            } catch (RemoteException e) {
                sm.mHandler.mResultMsg = null;
            }
            //被唤醒之后,返回
            Message resultMsg = sm.mHandler.mResultMsg;
            sm.recycle();
            return resultMsg;
        }

至此fullyConnectSync的全流程我们已经分析完成了,这里通过 fullyConnectSync 建立全连接省略了中间 “CMD_CHANNEL_HALF_CONNECTED” 消息回复过程,并且通过SyncMessenger,实现了在对服务端完成connect操作后才返回。这其中SyncMessenger的起到了一个中间桥梁的作用,虽然客户端省略了“CMD_CHANNEL_HALF_CONNECTED” 的流程,但是AsyncChannel的服务端依然需要处理 “CMD_CHANNEL_FULL_CONNECTION”。整个过程的大致流程图如下:

在这里插入图片描述



六 AsyncChannel消息机制使用实操演练以及原理分析

  其实AsyncChannel消息机制在前面讲解通道的使用和原理中已经有间接的讲解过了,好吗但是做戏要做全套,我们这里也不能例外,这里会从同步信息和异步消息两个方面入手来分析和讲解。这里有一点需要注意的是如果向实现客户端和服务端的双向信息通信,必须的前提是建立双向通道机制


6.1 AsyncChannel异步消息的使用

  何为异步,我们可以这里理解为当我们向对端发送一个消息过去之后,不会立马收到对方的回复,可能需要等待,这个等待是不确定的,可长可短。这里我们是在5.5章节的基础上进行的,即先建立快速双向通道连接,在这里我们就将快速双向通道的建立不表了,不然那就是太长了,小伙伴估计看到这里都基本看累了,说实话我写到这里都写累了。

6.1.1 客户端建立快速双向通道后向服务端发送异步消息

	//AsyncChannelSrcService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        async_mode = intent.getExtras().getInt("mode");
        Log.d(TAG, "async_mode : " + async_mode);
        if (async_mode == AsyncConstant.SINGAL_HALF_CONNECT) {// 单向通道通信
			......
        } else if (async_mode == AsyncConstant.MULTI_FULL_CONNECT) {// 在单向通道基础上建立双向通道
			......
        } else if (async_mode == AsyncConstant.FAST_MULTI_FULL_CONNECT) {//快速双向通道建立
			......
        }else if(async_mode == AsyncConstant.SEND_ASYNC_MESSAGE){//发送异步消息
            //1.先建立快速双向通道连接,这个不是这个章节的重点
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());
                    
            //2.连接成功以后,发送异步消息
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                mAsyncChannelSrc.sendMessage(AsyncConstant.ASYNC_MSG_REQ);//其实它也是我们的老熟人,前面分析的很多地方都有看到过,这里发送出去之后不会收到服务端的返回的信息,而是需要在Handroid中等待,所以是异步的
            }
        }else if(async_mode == AsyncConstant.SEND_SYNC_MESSAGE){//发送同步消息
			......
        }
        return super.onStartCommand(intent, flags, startId);
    }

6.1.2 服务端接收客户端发送的异步消息

  此时的客户端已经将异步消息发送出来了,轮到我们的服务端处理异步消息了,看看服务端是怎么处理的:

        public void handleMessage(Message msg) {
        	Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncConstant.ASYNC_MSG_REQ://接受客户端发送的异步消息请求
                Message reply = Message.obtain();
                Log.d(TAG, "ASYNC_MSG_REQ");
                reply.what = AsyncConstant.ASYNC_MSG_REPLY;
                reply.obj = "AsyncChannelDstService";
                try {
                    // 给Src客户端发送回应,回应的内容中what=ASYNC_MSG_REPLY,obj="AsyncChannelDstService"
                    msg.replyTo.send(reply);
                } catch (RemoteException e) {
                } 
                break;      	
            }
        }

这里可以看到服务端在收到异步消息之后,我们直接返回ASYNC_MSG_REPLY的消息给客户端了。

6.1.3 客户端接收服务端返回的异步消息

  当服务端收到异步信息之后,然后发送了一个ASYNC_MSG_REPLY的消息给客户端,所以我们将会在客户端中接收到服务端返回的ASYNC_MSG_REPLY异步信息,如下所示:

        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Dst : " + msg.what);
            switch (msg.what) {
                
                
            case AsyncConstant.ASYNC_MSG_REPLY://接收异步返回信息
                Log.d(TAG,"ASYNC_MSG_REPLY");
                break;

            default:
                break;
            }
            super.handleMessage(msg);
        }

6.1.4 AsyncChannel异步消息的使用结果演示

  整个测试代码已经部署OK了,下面就是见证奇迹的时刻了,让我们一起来看看演示的结果如何:


通过上述的相关打印,证明我们的异步消息发送已经完全OK了。

6.2 AsyncChannel异步消息的使用原理分析

  好吗,这里我们还是简单的来看看AsyncChannel的sendMessage异步通信方法,其实这个在我们的前面章节已经有说过了,但是还是看看不。

	//AsyncChannel.java
    public void sendMessage(int what) {
        Message msg = Message.obtain();
        msg.what = what;
        sendMessage(msg);
    }
    public void sendMessage(Message msg) {
        msg.replyTo = mSrcMessenger;
        try {
            mDstMessenger.send(msg);//这里的mDstMessenger是关键,这里的是服务端的Messenger
        } catch (RemoteException e) {
            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
        }
    }

比较简单,这个过程是不是和在单向通道基础上建立双向通道基础很相似,最终AsyncChannel通过成员变量mDstMessenger将消息发送给服务端。


6.3 AsyncChannel同步消息的使用

  何为同步,我们可以这里理解为当我们向对端发送一个消息过去之后,必须等到客户端的回复才善罢甘休,否则我不走了,我会一直等待下去,直到天荒地老海枯石烂。这里我们依然是在5.5章节的基础上进行的,即先建立快速双向通道连接,在这里我们就将快速双向通道的建立不表了,不然那就是太长了。

6.3.1 客户端建立快速双向通道后向服务端发送同步消息

	//AsyncChannelSrcService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        async_mode = intent.getExtras().getInt("mode");
        Log.d(TAG, "async_mode : " + async_mode);
        if (async_mode == AsyncConstant.SINGAL_HALF_CONNECT) {// 单向通道通信
			......
        } else if (async_mode == AsyncConstant.MULTI_FULL_CONNECT) {// 在单向通道基础上建立双向通道
			......
        } else if (async_mode == AsyncConstant.FAST_MULTI_FULL_CONNECT) {//快速双向通道建立
			......
        }else if(async_mode == AsyncConstant.SEND_ASYNC_MESSAGE){//发送异步消息
			......
        }else if(async_mode == AsyncConstant.SEND_SYNC_MESSAGE){//发送同步消息
            //1.先建立快速双向通道连接
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());
                    
            //2.连接成功以后,发送异步消息
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                Message  reply = mAsyncChannelSrc.sendMessageSynchronously(AsyncConstant.SYNC_MSG_REQ);//sendMessageSynchronously该方法是重点,将会阻塞在此直到服务端返回成功或者失败的信息
                Log.d(TAG,"reply msg : " + reply.toString());
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

6.3.2 服务端接收客户端发送的同步消息

  此时的客户端已经将同步消息发送出来了,轮到我们的服务端处理同步消息了,看看服务端是怎么处理的:

		//AsyncChannelDstService.java
        public void handleMessage(Message msg) {
        	Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncConstant.SYNC_MSG_REQ://收到同步请求信息
                Message reply_sync = Message.obtain();
                Log.d(TAG, "SYNC_MSG_REQ");
                reply_sync.what = AsyncConstant.SYNC_MSG_REPLY;
                reply_sync.obj = "AsyncChannelDstService";
                try {
                    // 给Src客户端发送回应,回应的内容中what=SYNC_MSG_REPLY,obj="AsyncChannelDstService"
                    msg.replyTo.send(reply_sync);
                } catch (RemoteException e) {
                }
                break;    	
            }
        }

这里我们可以看到和异步请求的处理类似,收到同步请求之后立即返回SYNC_MSG_REPLY的消息。

6.3.3 客户端接收服务端返回的同步消息

  当服务端收到异步信息之后,然后立马返回发送了一个SYNC_MSG_REPLY的消息给客户端,而我们的客户端是个痴情种一直在傻傻的等待着服务端的返回。

	//AsyncChannelSrcService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        async_mode = intent.getExtras().getInt("mode");
        Log.d(TAG, "async_mode : " + async_mode);
        if (async_mode == AsyncConstant.SINGAL_HALF_CONNECT) {// 单向通道通信
			......
        } else if (async_mode == AsyncConstant.MULTI_FULL_CONNECT) {// 在单向通道基础上建立双向通道
			......
        } else if (async_mode == AsyncConstant.FAST_MULTI_FULL_CONNECT) {//快速双向通道建立
			......
        }else if(async_mode == AsyncConstant.SEND_ASYNC_MESSAGE){//发送异步消息
			......
        }else if(async_mode == AsyncConstant.SEND_SYNC_MESSAGE){//发送同步消息
            //1.先建立快速双向通道连接
            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());
            int result = mAsyncChannelSrc.fullyConnectSync(this, mHandlerSrc,
                    AsyncChannelDstService.getHandler());
                    
            //2.连接成功以后,发送异步消息
            if (AsyncChannel.STATUS_SUCCESSFUL == result) {
                Log.d(TAG, "FAST_MULTI_FULL_CONNECT CONNECTED");
                Message  reply = mAsyncChannelSrc.sendMessageSynchronously(AsyncConstant.SYNC_MSG_REQ);//sendMessageSynchronously该方法是重点,将会阻塞在此直到服务端返回成功或者失败的信息,纵使海枯石烂我也要等到服务端的返回
                Log.d(TAG,"reply msg : " + reply.toString());
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

6.3.4 AsyncChannel同步消息的使用结果演示

  整个测试代码已经部署OK了,下面就是见证奇迹的时刻了,让我们一起来看看演示的结果如何:

在这里插入图片描述

通过上述的相关打印,证明我们的同步信息发送已经OK了,而我们痴情的客户端也收到了服务端的回复了。

6.2 AsyncChannel同步消息的使用原理分析

  通过前面的源码我们看到同步消息,重点使用到的方法是sendMessageSynchronously:

	//AsyncChannel.java
    public Message sendMessageSynchronously(int what) {
        Message msg = Message.obtain();
        msg.what = what;
        Message resultMsg = sendMessageSynchronously(msg);
        return resultMsg;
    }
    public Message sendMessageSynchronously(Message msg) {
        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
        return resultMsg;
    }


    private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
        SyncMessenger sm = SyncMessenger.obtain();//创建SyncMessenger对象sm
        try {
            if (dstMessenger != null && msg != null) {
                msg.replyTo = sm.mMessenger;//这个是关键点,将replyTo指向前面的sm对象
                synchronized (sm.mHandler.mLockObject) {
                	//将SYNC_MSG_REQ指令发送给服务端
                    dstMessenger.send(msg);
                    //等待被服务端唤醒
                    sm.mHandler.mLockObject.wait();
                }
            } else {
                sm.mHandler.mResultMsg = null;
            }
        } catch (InterruptedException e) {
            sm.mHandler.mResultMsg = null;
        } catch (RemoteException e) {
            sm.mHandler.mResultMsg = null;
        }
        //唤醒之后取出服务端的回复,返回给客户端
        Message resultMsg = sm.mHandler.mResultMsg;
        sm.recycle();
        return resultMsg;
    }
}

以上过程是不是有中似曾相识的感觉,你再品,细品?没错,这不就是建立快速双向通道中最关键的部分吗!其原理就是在客户端发送消息后,AsyncChannel内部创建一个SyncMessenger对象作为服务中间,待消息发送给服务端之后,SyncMessenger立马就进入阻塞状态,当服务端回应消息时,SyncMessenger被唤醒被解除阻塞,然后将结果返回给客户端。如上就是同步消息的基本原理。



七 AsyncChannel消息跨进程使用实操演练以及原理分析

  正准备开始写本篇章的结语了,可是突然发现我们的AsyncChannel一大特点跨进程传递消息还没有安排上呢,那必须整啊。必须全套的把AsyncChannel庖丁解牛了才成不是。先不说原理,先来看看AsyncChannel跨进程传递消息怎么用吗。


7.1 AsyncChannel消息跨进程使用

  人狠话不多,直接来点硬的,上实例演示。

7.1.1 先启动客户端

  为啥这里不需要启动服务端呢?这个AsyncChannel会通过bindService的方式进行启动的。

	//MainActivity.java
    public void do_asyncchannel_remote_conect(){
        // 启动Src端
        Intent mAsyncSrc = new Intent();
        mAsyncSrc.setComponent(new ComponentName(MainActivity.this,
                AsyncChannelSrcService.class));
        mAsyncSrc.putExtra("mode", AsyncConstant.REMOTE_CONNECT);
        startService(mAsyncSrc);
        
    }

7.1.2 客户端初始化流程

	//AsyncChannelSrcService.java
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        async_mode = intent.getExtras().getInt("mode");
        Log.d(TAG, "async_mode : " + async_mode);
        if (async_mode == AsyncConstant.SINGAL_HALF_CONNECT) {// 单向通道通信
			.....
        } else if (async_mode == AsyncConstant.MULTI_FULL_CONNECT) {// 在单向通道基础上建立双向通道
			......
        } else if (async_mode == AsyncConstant.FAST_MULTI_FULL_CONNECT) {// 快速双向通道建立
			......
        } else if (async_mode == AsyncConstant.SEND_ASYNC_MESSAGE) {// 发送异步消息
			......
        } else if (async_mode == AsyncConstant.SEND_SYNC_MESSAGE) {// 发送同步消息
			......
        } else if (async_mode == AsyncConstant.REMOTE_CONNECT) {// 跨进程的消息传递

            mAsyncChannelSrc = new AsyncChannel();
            mHandlerThreadSrc = new HandlerThread("syncSrc");
            mHandlerThreadSrc.start();
            mHandlerSrc = new HandlerSrc(mHandlerThreadSrc.getLooper());

            mAsyncChannelSrc
                    .connect(AsyncChannelSrcService.this, mHandlerSrc,
                            "com.itgentalman.asyncchannelfun",//远程服务的包名
                            "com.itgentalman.asyncchannelfun.AsyncChannelRemoteDstService");//远程服务的类信息
            remoteServiceBinderStatus = 0;
        }
        return super.onStartCommand(intent, flags, startId);
    }

  客户端调用AsyncChannel的connect方法,传入自己的Handler和远程服务端的包名,类名然后就可以开启跨进程通信之路了。

7.1.3 远程服务端相关代码和初始化流程

  这里有一个小技巧,我们可以在同一个工程的AndroidManifest.xml中对AsyncChannelRemoteDstService进行如下的配置,那么该服务就将运行在另外一个进程了:

        <service android:name="com.itgentalman.asyncchannelfun.AsyncChannelRemoteDstService"
            android:process=":remote"
        />

在这里插入图片描述

服务端的代码如下:

//AsyncChannelRemoteDstService.java
public class AsyncChannelRemoteDstService extends Service {

    private static final String TAG = "ASYNC/RDst";
    private static AsyncChannel mAsyncChannelDst = null;
    private static HandlerDst mHandlerDst = null;
    private static Context mContext;
    private static HandlerThread mHandlerThreadDst;
    private final Messenger mMessenger;

    public AsyncChannelRemoteDstService() {
        Log.e(TAG, "AsyncChannelRemoteDstService");
        mAsyncChannelDst = new AsyncChannel();
        mHandlerThreadDst = new HandlerThread("syncDst");
        mHandlerThreadDst.start();
        mHandlerDst = new HandlerDst(mHandlerThreadDst.getLooper());
        mContext = this;
        mMessenger = new Messenger(mHandlerDst);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        IBinder binder = mMessenger.getBinder();
        if (binder == null) {
            Log.d(TAG, "onBind null");
        }
        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    public static class HandlerDst extends Handler {

        public HandlerDst(Looper looper) {
            // TODO Auto-generated constructor stub
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Src : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                Log.d(TAG, "CMD_CHANNEL_FULL_CONNECTION");
                // 服务端接收到双向通道的建立请求
                // 如果同意客户端建立,则需要服务端向AsyncChannel申请连接请求
                mAsyncChannelDst.connect(mContext, mHandlerDst, msg.replyTo);
                // 全连接已经建立,发送 "CMD_CHANNEL_FULLY_CONNECTED" 给AsyncChannelSrcService
                mAsyncChannelDst.replyToMessage(msg,
                        AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                        AsyncChannel.STATUS_SUCCESSFUL);

                break;

            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                Log.d(TAG, "CMD_CHANNEL_HALF_CONNECTED");

                break;
                
            case AsyncConstant.ASYNC_TEST:
                Log.d(TAG, "ASYNC_TEST");
                break;
                
                default:
                    break;
            }
        }
    };

}

7.1.4 客户端处理远程服务端的消息

  当客户端和远程服务端建立连接后,将会收到两次的CMD_CHANNEL_HALF_CONNECTED返回,这个地方一定要注意,至于为什么会收到两次返回,我们将会在源码讲解中分析。

		//AsyncChannelSrcService.java
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d(TAG, "handleMessage from Dst : " + msg.what);
            switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                Log.d(TAG, "CMD_CHANNEL_HALF_CONNECTED");
                // Src客户端单项通道建立完成,也有可能是和远端,继续申请双向通道
                Log.d(TAG, "arg1 : " + msg.arg1);
                remoteServiceBinderStatus ++;
                //该处理逻辑仅针对跨进程连接,为啥会接收到两次呢后续会介绍
                if (async_mode == AsyncConstant.REMOTE_CONNECT && (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) && (2 == remoteServiceBinderStatus)) {
                    Log.e(TAG,"sendMessage");
                    mAsyncChannelSrc
                            .sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);//建立全连接
                }else if(async_mode != AsyncConstant.REMOTE_CONNECT){
                    mAsyncChannelSrc
                    .sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                }

                break;



            default:
                break;
            }
            super.handleMessage(msg);
        }

7.1.5 AsyncChannel消息跨进程使用结果演示

  此时此刻整个AsyncChannel跨进程消息传递代码已经部署OK,万事俱备只欠东风了,让我们来看看实际的效果和我们预想的是不是一样的。
在这里插入图片描述

7.1.6 AsyncChannel消息跨进程使用总结

  好了整个使用流程已经完毕,我们总结下该流程是不是很类似在单向通道连接基础上建立双向通道,其中跨越进程通信的事情AsyncChannel已经帮我们封装好了!

7.2 AsyncChannel消息跨进程源码分析

  还记得我们在章节二中介绍的,Handler配合Messenger可以通过Binder跨进程通信吗!其实AsyncChannel能跨进程通信也是基于以上的基础,然后将流程封装在AsyncChannel内部而已。

  在章节二介绍Messenger时,通过例子”MessengerService”对Messenger做了解析,AsyncChannel在跨进程通信上与前文例子相似:在连接前,需要先bindService,在 onServiceConnected 时,拿到Service的Messenger并完成了对mDstMessenger的初始化,从而实现了单通道半连接。之后,双通道全连接的方式就与前文介绍的相同。

7.2.1 AsyncChannel跨进程信息传递通道建立

	//AsyncChannel.java
    public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
            String dstClassName) {
        if (DBG) log("connect srcHandler to dst Package & class E");
        
		// 由于有bindService操作,并且需要等待onServiceConnected回调,
    	// 因此实现了一个Runnable并且新开一个线程去完成这个操作
        final class ConnectAsync implements Runnable {
            Context mSrcCtx;
            Handler mSrcHdlr;
            String mDstPackageName;
            String mDstClassName;

            ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
                    String dstClassName) {
                mSrcCtx = srcContext;
                mSrcHdlr = srcHandler;
                mDstPackageName = dstPackageName;
                mDstClassName = dstClassName;
            }

            @Override
            public void run() {
                int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
                        mDstClassName);//见章节7.2.2
                replyHalfConnected(result);//第一次返回CMD_CHANNEL_HALF_CONNECTED
            }
        }

        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
        new Thread(ca).start();//新开启一个线程开启异步连接远程服务操作

        if (DBG) log("connect srcHandler to dst Package & class X");
    }

7.2.2 AsyncChannel.connectSrcHandlerToPackageSync

	//AsyncChannel.java
    public int connectSrcHandlerToPackageSync(
            Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
        if (DBG) log("connect srcHandler to dst Package & class E");

		//AsyncChannelConnection实现了ServiceConnection接口,在服务成功连接上时,自动调用onServiceConnected函数
        mConnection = new AsyncChannelConnection();//详见7.2.3

		//初始化AsyncChannel中source端相关的成员变量
        mSrcContext = srcContext;
        mSrcHandler = srcHandler;
        mSrcMessenger = new Messenger(srcHandler);


        mDstMessenger = null;

        
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setClassName(dstPackageName, dstClassName);
        //绑定远程服务端
        boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
        return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
    }

  connectSrcHandlerToPackageSync 完成了对AsyncChannel中基本成员变量的初始化,与服务端相关的mDstMessenger变量需要等待 onServiceConnected 回调后再进行初始化:

7.2.3 AsyncChannelConnection

    class AsyncChannelConnection implements ServiceConnection {
        AsyncChannelConnection() {
        }

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
        	//绑定服务端成功,得到远程Messenger的代理端
            mDstMessenger = new Messenger(service);
            replyHalfConnected(STATUS_SUCCESSFUL);//又一次返回CMD_CHANNEL_HALF_CONNECTED
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            replyDisconnected(STATUS_SUCCESSFUL);
        }
    }

  还记得我们在演示的时候说的为什么客户端会收到两次CMD_CHANNEL_HALF_CONNECTED的消息吗,通过上述的源码解析我想大伙应该明白了。总的来说跨进程或同进程Service的AsyncChannel连接与普通的相同,只不过多了一步bindService的操作,并且mDstMessenger 需要等待 onServiceConnected 回调后才能完成初始化。



AsyncChannel总结

  至此,AsyncChannel的全场景分析完毕,下面我们再来回顾下本章我们主要讲解了什么,主要有如下三方面:

  • 分析了和演示了AsyncChannel为什么可以在单进程或不同进程间实现消息传递,通俗点来讲就是可以跨进程通信
  • 分析了和演示了AsyncChannel单/双通道的建立和通信
  • 分析和演示了AsyncChannel消息的传递

至此AsyncChannel分析完毕,我们也可以继续分析Android以太网的框架层了。最后附上演示代码的路径,大伙如果感兴趣可以下载下来看看AsyncChannelFun

最后附上参考博客:
https://blog.csdn.net/u010961631/article/details/48179305
https://blog.csdn.net/yangwen123/article/details/10917795
https://blog.csdn.net/qq_14978113/article/details/80701588

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页