AsyncChannel的工作机制

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.csdn.net/sjz4860402/article/details/78524091

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值