这篇文章主要介绍Android AsyncChannel、Messenger原理及其应用实例
注:文章参考的是Andrdoid 8.0源码
AsyncChannel 简介
AsyncChannel的源码位于 frameworks/base/core/java/com/android/internal/util/AsyncChannel.java,是对Handler和Messenger的一个扩展,用于单个进程内(包含不同线程间)或两个进程间的通信。在不同的进程间,AsyncChanne实质上使用的是IMessenger通过Binder的形式对消息进行发送和接收,当然,这种方式对单个进程内非remove service同样适用。
在Android系统中,有很多地方都使用了这个工具类,如ConnectivityService与NetworkAgent的通信,WifiServiceImpl与WifiStateMachine之间的通信等。由于AsyncChannel属于系统内部源码,三方应用无法直接进行使用,但我们可以学习思想,甚至可以自己简单实现并作用于我们的APP代码中。
AsyncChannel的主要特点:
Messenger
由于AsyncChannel实际上使用的是Handler和Messenger的机制,考虑到很多开发者对Messenger还不够了解,所以我们这里有必要先对这个工具做一个详细的解析。
Messenger的源码位于 frameworks/base/core/java/android/os/Messenger.java,是对Handler的一个再包装,并结合了Binder机制,使得跨进程间Message的传递和处理成为了可能。
成员变量及构造函数:
主要成员变量是IMessenger类型的mTarget,该变量可以通过Handler或者IBinder进行初始化,分别应用于同进程消息或者跨Service(remote或者非remote)消息发送:
// IMessenger类型,mTarget代表message的目的端
private final IMessenger mTarget;
// 初始化mTarget并指向Handler中的IMessenger
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
// 初始化mTarget并指向IBinder的实际类型
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
Messenger的发送函数:
无论是否跨进程,Messenger实际上都是利用IMessenger进行消息发送的:
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
IMessenger:
源码位于 frameworks/base/core/java/android/os/IMessenger.aidl,其目的是通过AIDL使用Binder机制对Message进行发送:
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
通过Handler对mTarget进行初始化:
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) {
// 由于存在Binder调用,初始化sendingUid为发送端uid
msg.sendingUid = Binder.getCallingUid();
// 通过Handler自身发送消息并进行处理
Handler.this.sendMessage(msg);
}
}
通过Service IBinder对mTarget进行初始化:
这里以Android源码中针对Messenger的测试case作为例子对其过程进行解析,源码路径:
- frameworks/base/core/tests/coretests/src/android/os/MessengerService.java
- frameworks/base/core/tests/coretests/src/android/os/MessengerTest.java
Service端实现(MessengerService.java):
public class MessengerService extends Service {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 在这里处理client端发送的消息
}
};
// 定义一个Messenger,Messenger中的mTarget即为mHandler中的mMessenger
private final Messenger mMessenger = new Messenger(mHandler);
@Override
public IBinder onBind(Intent intent) {
// 返回mHandler中的mMessenger
return mMessenger.getBinder();
}
}
Client端实现(MessengerTest.java):
通过 bindService 与MessengerService建立连接,连接完成后,对Messenger进行初始化:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (MessengerTest.this) {
// 通过IBinder类型的service对Messenger中的mTarget进行初始化
mServiceMessenger = new Messenger(service);
MessengerTest.this.notifyAll();
}
}
public void onServiceDisconnected(ComponentName name) {
// service断开后,对应的messenger也不会再有作用
mServiceMessenger = null;
}
};
因此,这样初始化后,下面的几个变量就可以看做是同一个对象的引用,即:
MessengerTest.mServiceMessenger.mTarget
= MessengerService.mMessenger.mTarget
= MessengerService.mHandler.mMessenger
Messenger小结:
实质上是通过IMessenger利用Binder机制进行消息的发送。Handler中实现了IMessenger中的 send 函数,并在其中调用Handler自身的 sendMessage 函数进行消息处理;Service中可以定义自己的Handler和对应的Messenger,并在onBind时返回Handler中MessengerImpl的实例,Client端在 onServiceConnected 函数中通过IBinder对象对Client中Messenger进行初始化,之后,Client端就可以通过该Messenger将消息发送到Service中的Handler进行处理了。Client和Service间通过Messenger进行消息传递的大致流程如下图:
AsyncChannel成员常量和变量
注明:从分析AsyncChannel开始,我们称连接的首次发起端为"source端",被连接端为"destination端"
连接指令相关静态常量:
/** 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;
连接和消息状态相关静态常量:
/** 连接状态相关:成功半连接,成功连接,成功断开 */
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;
主要成员变量:
/** 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;
AsyncChannel连接过程 - 半连接(Half Connect)
Source端发起连接请求:
/** 发起连接 */
public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
// source端AsyncChannel变量初始化
connected(srcContext, srcHandler, dstMessenger);
// 发送"CMD_CHANNEL_HALF_CONNECTED"告知source端半连接已建立
replyHalfConnected(STATUS_SUCCESSFUL);
}
/** 初始化source端的成员变量,创建source端Messenger */
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();
}
/** 发送"CMD_CHANNEL_HALF_CONNECTED"告知source端半连接已建立 */
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);
}
半连接小结:
整个半连接过程由连接发起端(source端)完成,destination端没有任何消息告知。source端调用AsyncChannel的 connect 函数后,开始对AsyncChannel中变量的初始化,主要是对mDstMessenger的初始化。完成后source端Handler会收到 “CMD_CHANNEL_HALF_CONNECTED” 的消息告知半连接已经完成,之后,source端就可以通过该AsyncChannel与destination进行通信。这种通信方式是单向的,只能由source端主动向destination推送消息并获取其回复,destination无法主动向source推送消息。这个连接过程可以由下面的时序图来表示:
AsyncChannel连接过程 - 全连接(Full Connect)
AsyncChannel的全连接建立主要有以下两种方式:
下面对这两种全连接建立方式分别作解析。
方式一(在半连接的基础上由source端发送 “CMD_CHANNEL_FULL_CONNECTION” 请求全连接):
这个过程主要是由AsyncChannel使用者自己完成,我们以ConnectivityService与NetworkAgent之间的通信进行举例,例子的关键源码路径如下:
- frameworks/base/services/core/java/com/android/server/ConnectivityService.java
- frameworks/base/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
- frameworks/base/core/java/android/net/NetworkAgent.java
首先,ConnectivityService作为连接主动端主动请求半连接:
/** ConnectivityService#handleRegisterNetworkAgent */
private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
// ...
// 通过NetworkAgentInfo中新创建的AsyncChannel主动请求连接
// mTrackerHandler是ConnectivityService中的Handler,作为source handler
// na.messenger是连接的对端,这里就是指 NetworkAgent
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
// ..
}
通过对半连接的了解,在 connect 之后,source handler就会收到 “CMD_CHANNEL_HALF_CONNECTED”。由于需要建立全连接,ConnectivityService在收到该消息后,立刻向半连接对端请求全连接:
/** ConnectivityService#NetworkStateTrackerHandler */
private class NetworkStateTrackerHandler extends Handler {
// ...
private boolean maybeHandleAsyncChannelMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
// 收到 "CMD_CHANNEL_HALF_CONNECTED",在下面的函数中处理
handleAsyncChannelHalfConnect(msg);
break;
}
// ...
}
/** ConnectivityService#handleAsyncChannelHalfConnect */
private void handleAsyncChannelHalfConnect(Message msg) {
// ...
else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkAgent connected");
// 发送全连接请求 "CMD_CHANNEL_FULL_CONNECTION"
mNetworkAgentInfos.get(msg.replyTo).asyncChannel.
sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
}
}
NetworkAgent在收到 “CMD_CHANNEL_FULL_CONNECTION” 请求后,便开始创建自己的AsyncChannel,并完成其初始化,连接的对端是ConnectivityService:
/** NetworkAgent#handleMessage */
public void handleMessage(Message msg) {
switch (msg.what) {
// 收到由ConnectivityService发来的全连接请求
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
if (mAsyncChannel != null) {
log("Received new connection while already connected!");
} else {
if (VDBG) log("NetworkAgent fully connected");
// 创建自己的AsyncChannel
AsyncChannel ac = new AsyncChannel();
// msg.replayTo 就是NetworkAgentInfo.asyncChannel中的mSrcMessenger,
// 消息的处理者就是ConnectivityService.mTrackerHandler
ac.connected(null, this, msg.replyTo);
// 全连接已经建立,发送 "CMD_CHANNEL_FULLY_CONNECTED" 给ConnectivityService
ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_SUCCESSFUL);
// ...
}
break;
// ...
}
从上述的过程可以看出,建立全连接的方式就是连接的两端都需要创建自己的AsyncChannel,并且每个AsyncChannel中都保存着对端的Messenger。这种全连接的方式可以通过下面流程图进行表示:
方式二:source端直接调用AsyncChannel的 fullyConnectSync 进行全连接
从 fullyConnectSync 这个函数名,可以知道这是一个同步的操作,AsyncChannel中使用了SyncMessenger这个静态内部类来实现这个同步连接的操作。直接从 fullyConnectSync 这个函数开始分析。
fullyConnectSync 中对source端AsyncChannel进行了初始化,并发送 “CMD_CHANNEL_FULL_CONNECTION” 同步消息请求全连接:
/** AsyncChannel#fullyConnectSync*/
public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
// 只是调用了connected函数,对source端AsyncChannel成员变量进行初始化
int status = connectSync(srcContext, srcHandler, dstHandler);
if (status == STATUS_SUCCESSFUL) {
// 发送"CMD_CHANNEL_FULL_CONNECTION"同步消息给destination
// 同步消息,需要等待回应后才继续
Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
status = response.arg1;
}
return status;
}
sendMessageSynchronously 实际使用的是 “SyncMessenger.sendMessageSynchronously”:
/** AsyncChannel.SyncMessenger#sendMessageSynchronously*/
private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
// 获取一个SyncMessenger对象,启动消息接收者SyncHandler
SyncMessenger sm = SyncMessenger.obtain();
try {
if (dstMessenger != null && msg != null) {
msg.replyTo = sm.mMessenger;
// 获取对象锁:"sm.mHandler.mLockObject"
synchronized (sm.mHandler.mLockObject) {
// 发送 "CMD_CHANNEL_FULL_CONNECTION"
dstMessenger.send(msg);
// 释放该锁并中断等待notify
sm.mHandler.mLockObject.wait();
}
} else {
sm.mHandler.mResultMsg = null;
}
} catch (InterruptedException e) {
sm.mHandler.mResultMsg = null;
} catch (RemoteException e) {
sm.mHandler.mResultMsg = null;
}
// 被CPU唤醒,消息回复已经收到,返回该回复
Message resultMsg = sm.mHandler.mResultMsg;
sm.recycle();
return resultMsg;
}
通过SyncMessenger向对端发送完消息后,对端如果已经完成了 connect 的操作或者拒绝连接,都应该回复消息。由于发送消息前设定了msg.replyTo = sm.mMessenger
,因此回复的消息会被SyncManager中的SyncHandler处理:
/** AsyncChannel.SyncManager#SyncHandler#handleMessage*/
public void handleMessage(Message msg) {
mResultMsg = Message.obtain();
mResultMsg.copyFrom(msg);
// 获取mLockObject对象锁
synchronized(mLockObject) {
// 唤醒在mLockObject这个锁上等待的线程
mLockObject.notify();
}
}
从上面的流程可以看出,通过 fullyConnectSync 建立全连接省略了中间 “CMD_CHANNEL_HALF_CONNECTED” 消息回复过程,并且通过SyncMessenger,实现了在对端完成connect操作后才返回。因此,这个过程不需要AsyncChannel的主动连接者进行其他操作就可以完成全连接。然而,AsyncChannel的对端依然需要处理 “CMD_CHANNEL_FULL_CONNECTION”。整个过程的大致流程图如下:
AsyncChannel的跨进程消息传递
由于AsyncChannel内部使用的是Messenger,且Handler和Messenger都已经支持了Binder通信方式,因此,AsyncChannel同样可以利用在跨进程通信上。
前文在介绍Messenger时,通过例子”MessengerService”对Messenger做了解析,AsyncChannel在跨进程通信上与前文例子相似:在连接前,需要先bindService,在 onServiceConnected 时,拿到Service的Messenger并完成了对mDstMessenger的初始化,从而实现了半连接。之后,全连接的方式就与前文介绍的相同。
/** AsyncChannel#connect*/
public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
String dstClassName) {
// 由于有bindService操作,并且需要等待onServiceConnected回调,
// 因此实现了一个Runnable并且新开一个线程去完成这个操作
final class ConnectAsync implements Runnable {
// ...
@Override
public void run() {
// 完成对AsyncChannel中除mDstMessenger的成员变量的初始化
// 并执行bindService操作
int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
mDstClassName);
replyHalfConnected(result);
}
}
ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName);
// 新开一个线程执行异步连接操作
new Thread(ca).start();
}
connectSrcHandlerToPackageSync 完成了对AsyncChannel中基本成员变量的初始化,与destination相关的mDstMessenger变量需要等待 onServiceConnected 回调后再进行初始化:
/** AsyncChannel#connectSrcHandlerToPackageSync */
public int connectSrcHandlerToPackageSync(
Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
mConnection = new AsyncChannelConnection();
/* 初始化AsyncChannel source相关的变量 */
mSrcContext = srcContext;
mSrcHandler = srcHandler;
mSrcMessenger = new Messenger(srcHandler);
// 对端Messenger等待onServiceConnected后再进行初始化
mDstMessenger = null;
// bindService
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(dstPackageName, dstClassName);
boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
}
AsyncChannelConnection#onServiceConnected 会在Service bind成功后被回调,在这个函数中,完成了对 mDstMessenger 的初始化,到此,半连接完成:
/** AsyncChannel#AsyncChannelConnection */
class AsyncChannelConnection implements ServiceConnection {
AsyncChannelConnection() {
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// service bind成功,初始化mDstMessenger
mDstMessenger = new Messenger(service);
replyHalfConnected(STATUS_SUCCESSFUL);
}
// ...
}
跨进程或同进程Service的AsyncChannel连接与普通的相同,只不过多了一步bindService的操作,并且mDstMessenger 需要等待 onServiceConnected 回调后才能完成初始化。
总结
AsyncChannel 结合Messenger,实现了两个进程或线程间Handler消息的传递。这篇文章主要介绍了Messenger的原理、AsyncChannel的两种连接方式:半连接和全连接、AsyncChannel跨进程连接等,虽然这个工具类无法直接使用,但如果应用中有需要,仍然可以借鉴其原理作适合的简单实现。