這篇文章主要介紹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的主要特點:
- 可以在單進程或不同進程間實現消息傳遞
- 支持建立單向通信或雙向通信
- 是對Handler,Messenger的一種包裝,並沒有實現額外的通信方式
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” 請求全連接
- source端直接調用AsyncChannel的 fullyConnectSync 進行全連接
下面對這兩種全連接建立方式分別作解析。
方式一(在半連接的基礎上由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跨進程連接等,雖然這個工具類無法直接使用,但如果應用中有需要,仍然可以借鑑其原理作適合的簡單實現。