Messenger 簡介
Messenger “信使”,顧名思義,它的作用就是傳遞資訊。
Messenger 有兩個構造函數:
- 以 Handler 為參數
- 以 Binder 為參數
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
看下
Handler.getIMessenger()
源碼:
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
這個
IMessanger
應該也是個 AIDL 生成的類吧,看下源碼,果然是:
public interface IMessenger extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
android.os.IMessenger {
private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static android.os.IMessenger asInterface(...}
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {...}
private static class Proxy implements android.os.IMessenger {...}
public void send(android.os.Message msg)
throws android.os.RemoteException;
}
IMessenger
是 AIDL 生成的跨程序接口,裡面定義了一個發送消息的方法:
public void send(android.os.Message msg)
throws android.os.RemoteException;
Handler
中
MessengerImpl
實作了這個方法,就是使用 Handler 将消息發出去:
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
這就解釋了為什麼我們的消息來的時候會出現在
Handler.handlerMessage()
中。
接着再看下 Messenger 另一個重要的方法,
send()
:
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
Messenger
中持有一個
IMessenger
的引用,在構造函數中可以通過
Handler
或者
Binder
的形式獲得最終的
IMessenger
實作,然後調用它的
send()
方法。
Messenger
其實就是 AIDL 的簡化版,它把接口都封裝好,我們隻需在一個程序建立一個
Handler
傳遞給 Messenger,Messenger 幫我們把消息跨程序傳遞到另一個程序,我們在另一個程序的 Handler 在處理消息就可以了。
Messenger 的使用
Messenger 的使用需要結合
Handler
,
Message
,
Bundle
。
下面我們将寫一個用戶端跨程序發送消息到服務端的例子,服務端在收到消息後會回複,由于在 Messenger 中一個對象對應一個 Handler,是以我們需要在用戶端、服務端分别建立一個 Messenger:
服務端在收到消息後會使用
Message.replyTo
對應的信使回複消息。
服務端
服務端隻需要建立一個 Messenger 對象,然後給它傳遞一個 Handler,在 Handler 中處理消息:
public class MessengerService extends BaseService {
private final String TAG = this.getClass().getSimpleName();
Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(final Message msg) {
if (msg != null && msg.arg1 == ConfigHelper.MSG_ID_CLIENT) {
if (msg.getData() == null) {
return;
}
String content = (String) msg.getData().get(ConfigHelper.MSG_CONTENT); //接收用戶端的消息
LogUtils.d(TAG, "Message from client: " + content);
//回複消息給用戶端
Message replyMsg = Message.obtain();
replyMsg.arg1 = ConfigHelper.MSG_ID_SERVER;
Bundle bundle = new Bundle();
bundle.putString(ConfigHelper.MSG_CONTENT, "聽到你的消息了,請說點正經的");
replyMsg.setData(bundle);
try {
msg.replyTo.send(replyMsg); //回信
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
@Nullable
@Override
public IBinder onBind(final Intent intent) {
return mMessenger.getBinder();
}
}
用戶端
public class IPCTestActivity extends BaseActivity {
private final String TAG = this.getClass().getSimpleName();
@BindView(R.id.tv_result)
TextView mTvResult;
@BindView(R.id.btn_add_person)
Button mBtnAddPerson;
@BindView(R.id.et_msg_content)
EditText mEtMsgContent;
@BindView(R.id.btn_send_msg)
Button mBtnSendMsg;
/**
* 用戶端的 Messenger
*/
Messenger mClientMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(final Message msg) {
if (msg != null && msg.arg1 == ConfigHelper.MSG_ID_SERVER){
if (msg.getData() == null){
return;
}
String content = (String) msg.getData().get(ConfigHelper.MSG_CONTENT);
LogUtils.d(TAG, "Message from server: " + content);
}
}
});
//服務端的 Messenger
private Messenger mServerMessenger;
private ServiceConnection mMessengerConnection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
mServerMessenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(final ComponentName name) {
mServerMessenger = null;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
ButterKnife.bind(this);
bindAIDLService();
bindMessengerService();
}
private void bindMessengerService() {
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mMessengerConnection, BIND_AUTO_CREATE);
}
@OnClick(R.id.btn_send_msg)
public void sendMsg() {
String msgContent = mEtMsgContent.getText().toString();
msgContent = TextUtils.isEmpty(msgContent) ? "預設消息" : msgContent;
Message message = Message.obtain();
message.arg1 = ConfigHelper.MSG_ID_CLIENT;
Bundle bundle = new Bundle();
bundle.putString(ConfigHelper.MSG_CONTENT, msgContent);
message.setData(bundle);
message.replyTo = mClientMessenger; //指定回信人是用戶端定義的
try {
mServerMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mMessengerConnection);
}
}
使用小結
可以看到用戶端的操作主要有 3 步:
- 建立用戶端的
,傳遞一個Messenger
處理消息。Handler
-
,在bindService
回調中拿到服務端的ServiceConnection
Messenger
- 發送消息
-
消息池裡擷取一個空閑消息對象Message.obtain()
- 使用
設定資料message.setData(bundle)
- 指定回信的信使
message.replyTo = mClientMessenger
- 調用服務端信使,發射!
mServerMessenger.send(message)
-
總結
Messenger 對 AIDL 進行了封裝,也就是對 Binder 的封裝,我們可以使用它的實作來完成基于消息的跨程序通信,就和使用 Handler 一樣簡單。
使用步驟:
- 用戶端建立一個
,傳遞一個 Handler 處理消息Messenger
- 服務端也一樣
如果需要回信,給
Message
設定一個用于回信的 Messenger 即可:
message.replyTo = mClientMessenger;
用戶端在調用
send()
方法之後,就會走 Binder 跨程序通信機制 ,最後到服務端的 Handler 中得到處理。