Android -- AsyncChannel
在学习Android Wifi部分的源码时,发现WifiManager和WifiService之间使用了AsyncChannel来进行通信,AsyncChannel用于两个Handler之间的通信,这里简单介绍下AsyncChannel的实现机制,文章中如有错误的地方,请批评指出,小弟感激不尽。
首先介绍下AsyncChannel:
AsyncChannel用于两个Handler之间通信,它并不关心这两个Handler是否寸在于同一进程。AsyncChannel有两种使用模式:
1)、简单的request/reply模式:该模式下Server不关心Client的任何信息,它只负责处理来自Client的请求。
2)、与request/reply模式不同,第二种使用场景Server需要区分不同的Client,它需要知道它连接的是哪个Client。例如,Server会主动地向Client发送一些消息;或者Server会为每个Client维护一些状态信息。
WifiManager和WifiService之间采用的是第二种使用模式,下面就以此例来介绍如何使用不同的两个Handler来进行通信(基于andr 6.0)。
WiFiManager在其构造函数中创建AsyncChannel:
- public WifiManager(Context context, IWifiManager service) {
- mContext = context;
- mService = service;
- mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- init();
- }
- private void init() {
- synchronized (sThreadRefLock) {
- if (++sThreadRefCount == 1) {
- Messenger messenger = getWifiServiceMessenger(); //获取到一个代表Handler对象的引用,我们可以用它来发送消息
- if (messenger == null) {
- sAsyncChannel = null;
- return;
- }
- sHandlerThread = new HandlerThread("WifiManager");
- sAsyncChannel = new AsyncChannel();
- sConnected = new CountDownLatch(1);
- sHandlerThread.start();
- Handler handler = new ServiceHandler(sHandlerThread.getLooper());
- sAsyncChannel.connect(mContext, handler, messenger);
- try {
- sConnected.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted wait at init");
- }
- }
- }
- }
1)、该函数首先通过AIDL机制(Binder)调用 WifiService的getWifiServiceMessenger()函数,创建一个Messenger对象。Messenger继承自Parcelable类。其内部维护了一个
- private final IMessenger mTarget;
对象,它可用于进程间的Binder通信。通过以下两个函数可知:
- /**
- * Create a new Messenger pointing to the given Handler. Any Message
- * objects sent through this Messenger will appear in the Handler as if
- * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
- * been called directly.
- *
- * @param target The Handler that will receive sent messages.
- */
- public Messenger(Handler target) {
- mTarget = target.getIMessenger();
- }
- /**
- * Send a Message to this Messenger's Handler.
- *
- * @param message The Message to send. Usually retrieved through
- * {@link Message#obtain() Message.obtain()}.
- *
- * @throws RemoteException Throws DeadObjectException if the target
- * Handler no longer exists.
- */
- public void send(Message message) throws RemoteException {
- mTarget.send(message);
- }
再看init()函数,Client中的Handler运行在名为“WifiManager”的sHandlerThread线程中,调用connect()函数进行连接。这里,handler是WifiManager中ServiceHandler对象,messenger对象是一个WifiService中ClientHandler的一个引用,从这里就可以看出此次异步通道通信之间的两个Handler对象分别是什么了:一个是WifiManager中的ServiceHandler对象;另一个是WifiService中的ClientHandler对象。
进入AsyncChannel::connect()函数:
- /**
- * Connect handler and messenger.
- *
- * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
- * msg.arg1 = status
- * msg.obj = the AsyncChannel
- *
- * @param srcContext
- * @param srcHandler
- * @param dstMessenger
- */
- public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
- if (DBG) log("connect srcHandler to the dstMessenger E");
- // We are connected
- connected(srcContext, srcHandler, dstMessenger);
- // Tell source we are half connected
- replyHalfConnected(STATUS_SUCCESSFUL);
- if (DBG) log("connect srcHandler to the dstMessenger X");
- }
- /**
- * Connect handler to messenger. This method is typically called
- * when a server receives a CMD_CHANNEL_FULL_CONNECTION request
- * and initializes the internal instance variables to allow communication
- * with the dstMessenger.
- *
- * @param srcContext
- * @param srcHandler
- * @param dstMessenger
- */
- public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
- if (DBG) log("connected srcHandler to the dstMessenger E");
- // Initialize source fields
- mSrcContext = srcContext;
- mSrcHandler = srcHandler;
- mSrcMessenger = new Messenger(mSrcHandler);
- // Initialize destination fields
- mDstMessenger = dstMessenger;
- if (DBG) log("connected srcHandler to the dstMessenger X");
- }
- /**
- * Reply to the src handler that we're half connected.
- * see: CMD_CHANNEL_HALF_CONNECTED for message contents
- *
- * @param status to be stored in msg.arg1
- */
- private void replyHalfConnected(int status) {
- Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
- msg.arg1 = status;
- msg.obj = this;
- msg.replyTo = mDstMessenger;
- /*
- * Link to death only when bindService isn't used.
- */
- if (mConnection == null) {
- mDeathMonitor = new DeathMonitor();
- try {
- mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);//若Server端所在的进程已经死掉,来通知Client端进程进行一些后期处理
- } catch (RemoteException e) {
- mDeathMonitor = null;
- // Override status to indicate failure
- msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
- }
- }
- mSrcHandler.sendMessage(msg);
- }
回到WifiManager中看ServiceHandler怎样处理此消息:
- private static class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message message) {
- Object listener = removeListener(message.arg2);
- switch (message.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- } else {
- Log.e(TAG, "Failed to set up channel connection");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- sAsyncChannel = null;
- }
- sConnected.countDown();
- break;
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- // Ignore
- break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- sAsyncChannel = null;
- getLooper().quit();
- break;
- /* ActionListeners grouped together */
- }
- }
- }
- /**
- * Handles client connections
- */
- private class ClientHandler extends Handler {
- ClientHandler(android.os.Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
- // We track the clients by the Messenger
- // since it is expected to be always available
- mTrafficPoller.addClient(msg.replyTo);
- } else {
- Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
- }
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
- if (DBG) Slog.d(TAG, "Send failed, client connection lost");
- } else {
- if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
- }
- mTrafficPoller.removeClient(msg.replyTo);
- break;
- }
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- AsyncChannel ac = new AsyncChannel();
- ac.connect(mContext, this, msg.replyTo);
- break;
- }
- }
- }
处理的结果是再次调用AsyncChannel::connect(mContext, this, msg.replyTo);
这里this代表的是WifiService::ClientHandler对象,msg.replyTo代表WifiManager::ServiceHandler对象。connect函数的实现上面已经看过,即向ClientHandler发送CMD_CHANNEL_HALF_CONNECTED消息,参数是STATUS_SUCCESSFUL;
相应的处理的是:mTrafficPoller.addClient(msg.replyTo);
将WifiMananger的ServiceHandler加入到Wifi系统的流量轮询管理机制当中,用于以后通知ServiceHandler不同的状态。到此,WifiManager和WifiService之间的一个基于AsyncChannel的双向通信连接就建立完成;至此,WifiManager就可以通过AsyncChannel来发送一些指令了:
- public void connect(int networkId, ActionListener listener) {
- if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
- validateChannel();
- sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
- }