AsyncChannel为两个不同的handler之间建立消息通道。本来两个handler之间也可以通过其handler机制互相发送接收message消息来通信,但是AsyncChannel封装了更多功能,考虑了同步异步操作,同进程或者不同进程间通信的问题,使得某些场景下的消息传递更加方便。AsyncChannel主要用在ConnectivityService框架中,涉及了wifi,mobile data,bluetooth,Tethering等模块。
此次分析AsyncChannel主要从两个方面来着手:
一.AsyncChannel的具体使用
二.同进程中,同步异步操作下,AsyncChannel的工作机制
二.跨进程下,AsyncChannel的工作机制
AsyncChannel的具体使用
对于使用者来说,不需要关注它的实现方式,但是需要自己判断使用场景,是同步还是异步,是同进程还是异进程,是单向连接还是双向连接。
1.如果需要实现异步,客户端要调用connect()方法,并实现CMD_CHANNEL_HALF_CONNECTED消息的handleMessage()处理,客户端handler主动向服务器端handler发出消息sendMessage(),服务器端handler不回应此消息,则为单向连接,如果需要服务器端回应消息,则接收端重复以上客户端的操作,发起connect(),这是双向连接;
2.如果需要实现同步,则客户端调用fullyConnectSync(),双向连接的话服务器端调用replyToMessage()即可,相对同步操作,异步的使用较为简单。
3.异进程下只提供了异步操作,使用上稍微复杂一点,需要借助AsyncChannelConnection来实现异进程的通讯(bindService),所以要实现对CMD_CHANNEL_HALF_CONNECTED和CMD_CHANNEL_DISCONNECTED的handleMessage()处理,方便监听当前的异进程通讯是否有问题。异进程的异步操作只是多了这个动作,其他的跟同进程下异步操作没什么区别。
AsyncChannel的同步使用例子:
客户端:DcTracker.java
服务器端:DataConnection.java
步骤一:客户端发起异步连接
DataConnection conn = DataConnection.makeDataConnection(mPhone,getPdpConnectionPoolSize(), this, mDcTesterFailBringUpAll, mDcc);
mDataConnections.put(getPdpConnectionPoolSize(), conn);
dcac = new DcAsyncChannel(conn, LOG_TAG);
int status = dcac.fullyConnectSync(mPhone.getContext(),this, conn.getHandler());
- 1
- 2
- 3
- 4
步骤二:服务器端收到CMD_CHANNEL_FULL_CONNECTION连接通知,如果收到此通知,只做自己的处理,不去回复客户端,那此次连接就是单向连接模式。如果服务器端接收到CMD_CHANNEL_FULL_CONNECTION连接通知后,也创建自己的AsyncChannel对象,并发起CMD_CHANNEL_FULL_CONNECTION连接,那此次的连接过程就是双向连接模式。以下就是双向连接的模式:
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
if (mAc != null) {
if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
} else {
mAc = new AsyncChannel();
mAc.connected(null, getHandler(), msg.replyTo);
if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
}
break;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
AsyncChannel的异步使用例子:
客户端:ConnectivityService.java
服务器端:NetworkFactory.java
步骤一:客户端发起异步连接
//NetworkFactory是一个handler,通过AsyncChannel与mTrackerHandler进行消息传递
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
mNetworkFactoryInfos.put(nfi.messenger, nfi);
nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
}
//接收来自AsyncChannel的CMD_CHANNEL_HALF_CONNECTED消息,做进一步处理
private boolean maybeHandleAsyncChannelMessage(Message msg) {
switch (msg.what) {
default:
return false;
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
handleAsyncChannelHalfConnect(msg);
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) nai.asyncChannel.disconnect();
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
handleAsyncChannelDisconnected(msg);
break;
}
}
return true;
}
//通过AsyncChannel发起的sendMessage()操作,把CMD_REQUEST_NETWORK消息发送到服务器端
private void handleAsyncChannelHalfConnect(Message msg) {
AsyncChannel ac = (AsyncChannel) msg.obj;
if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (!nri.isRequest()) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
}
} else {
loge("Error connecting NetworkFactory");
mNetworkFactoryInfos.remove(msg.obj);
}
}
......
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
步骤二:服务器端接收到CMD_REQUEST_NETWORK进行相应的处理,这里是单向连接,因此服务器端不需要再向客户端发起connect()操作。
public void handleMessage(Message msg) {
switch (msg.what) {
case CMD_REQUEST_NETWORK: {
handleAddRequest((NetworkRequest)msg.obj, msg.arg1);
break;
}
......
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
同步异步操作下,AsyncChannel的使用及工作机制
同步和异步操作下,AsyncChannel的工作流程时序图如下:
AsyncChannel的同步工作机制:
同步操作需要借助内部类SyncMessenger来实现,通过同步锁的机制来完成同步的过程。其具体的过程如下:
调用fullyConnectSync()建立连接的时候需要传入两个互相通信的Handler对象。
public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
int status = connectSync(srcContext, srcHandler, dstHandler);//初始化工作
//异步的过程
if (status == STATUS_SUCCESSFUL) {
Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
status = response.arg1;
}
return status;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
把Handler封装进Messenger,也就是初始化mSrcMessenger ,dstMessenger
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) {
// We are connected
connected(srcContext, srcHandler, dstMessenger);
return STATUS_SUCCESSFUL;
}
public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
// Initialize source fields
mSrcContext = srcContext;
mSrcHandler = srcHandler;
mSrcMessenger = new Messenger(mSrcHandler);
// Initialize destination fields
mDstMessenger = dstMessenger;
linkToDeathMonitor();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
把此次通信的目的Handler的封装类Messenger对象加入到SyncMessenger中,实现异步目的。
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();
try {
if (dstMessenger != null && msg != null) {
//发往服务器端的msg.replyTo为SyncHandler对象的封装,意味着服务器端send消息过来后,由SyncHandler的handleMessage()进行接收。
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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
SyncMessenger是AsyncChannel的内部类,它的主要作用是实现异步的目的。它自己维护了一个消息栈和一个消息发送线程mHandlerThread,通过内部类SyncHandler实现对上锁的消息进行解锁。
private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
private HandlerThread mHandlerThread;
- 1
- 2
消息入栈:
private static SyncMessenger obtain() {
SyncMessenger sm;
synchronized (sStack) {
if (sStack.isEmpty()) {
sm = new SyncMessenger();
sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
//创建具有消息循环队列的HandlerThread线程,并启动该线程
sm.mHandlerThread.start();
sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
sm.mMessenger = new Messenger(sm.mHandler);
} else {
sm = sStack.pop();
}
}
return sm;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
消息出栈:
private void recycle() {
synchronized (sStack) {
sStack.push(this);
}
}
SyncHandler接收到服务器端send过来的消息后,进行解锁。
public void handleMessage(Message msg) {
mResultMsg = Message.obtain();
mResultMsg.copyFrom(msg);//服务器端返回的msg
synchronized(mLockObject) {
mLockObject.notify(); //解锁
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在上面说到sendMessageSynchronously的时候,dstMessenger.send(msg)发送到服务器端后,如果服务器端有回应,也就是调用replyToMessage回应时,srcMsg.replyTo其实就是sm.mMessenger,所以srcMsg.replyTo.send(dstMsg)发送的消息是到SyncHandler的handleMessage()中处理。
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();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
以上就是同步AsyncChannel的工作流程,当我们调用fullyConnectSync()得到返回值时,就表示已经和服务器端建立连接成功,并获得了相应的结果。
同步发送机制总结:
调用者利用SyncMessenger的成员sendMessageSynchronously将要发送的消息msg送往dstMessenger所在的目的进程,由dstMessenger的handler进行处理,然后消息发送线程等待,dstMessenger的handler处理完毕接收到的消息后,要向msg.replyTo回送消息,这时由SyncHandler的handleMessage来处理该回复消息,它将唤醒在消息发送线程中等待的sendMessageSynchronously。从目的进程返回的msg在SyncHandler的handleMessage中拷贝给SyncHandler的mResultMsg,然后由sendMessageSynchronously返回给调用者。
AsyncChannel的异步工作机制:
异步操作的过程需要关注CMD_CHANNEL_HALF_CONNECTED消息的处理,使用者主动发起sendMessage()操作,其他的流程比较简单,这里就不细讲了。
跨进程下,AsyncChannel的工作机制
跨进程下AsyncChannel只提供了异步操作,没有同步操作过程。
这里主要通过Service的bindService方式获取不同进程的对端Handler,本质就是通过了Binder机制进行进程间通信。
其时序图如下:
以上时序图,序号13:connect()之后的流程跟1:connect()之后的流程一致,时序图中省略了这部分的流程,需要注意这一点。
为避免进程间通信耗时过长,阻塞关键进程,这里新建了一个线程用于获取对端的Handler。
public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
String dstClassName) {
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);
replyHalfConnected(result);
}
}
ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
new Thread(ca).start();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
初始化工作以及bindService。
public int connectSrcHandlerToPackageSync(
.......
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
通知调用者Handler,对端Handler的连接状态。通知消息为CMD_CHANNEL_HALF_CONNECTED,携带了相关连接信息:msg.arg1 = status,成功为STATUS_SUCCESSFUL。
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
调用者Handler接收到CMD_CHANNEL_HALF_CONNECTED消息后,判断是STATUS_SUCCESSFUL后,可以直接向对端Handler发起sendMessage操作。
以上就是个人对AsyncChannel的简单理解与分析,理解了这个AsyncChannel的机制,对于通讯模块的代码框架的研究会比较容易一些。
转自:https://blog.youkuaiyun.com/sjz4860402/article/details/78524091