前言
IPC 系列文章:
建議按順序閱讀纸淮。
Android IPC 之Service 還可以這么理解
Android IPC 之Binder基礎(chǔ)
Android IPC 之Binder應(yīng)用
Android IPC 之AIDL應(yīng)用(上)
Android IPC 之AIDL應(yīng)用(下)
Android IPC 之Messenger 原理及應(yīng)用
Android IPC 之服務(wù)端回調(diào)
Android IPC 之獲取服務(wù)(IBinder)
Android Binder 原理換個(gè)姿勢(shì)就頓悟了(圖文版)
前面從源碼+Demo角度詳盡分析了AIDL做修,可能會(huì)覺得AIDL文件的編寫略微有些麻煩模狭,本篇文章將分析AIDL 簡(jiǎn)化版 Messenger 原理及其應(yīng)用。
通過本篇文章,你將了解到:
1、Messenger 客戶端發(fā)送消息給服務(wù)端
2茵瀑、Messenger 服務(wù)端發(fā)送消息給客戶端
3、Messenger 底層原理
4躬厌、Message马昨、AIDL、Messenger區(qū)別與聯(lián)系
1扛施、Messenger 客戶端發(fā)送消息給服務(wù)端
與AIDL 類似偏陪,依然分別編寫服務(wù)端、客戶端邏輯煮嫌。
編寫服務(wù)端
public class MyService extends Service {
private String TAG = "ipc";
//創(chuàng)建Handler,用來處理客戶端發(fā)送過來的消息
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Bundle bundle = msg.getData();
int id = bundle.getInt("id");
Log.d(TAG, "receive id from client, id:" + id);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
//獲取IBinder 引用
return new Messenger(handler).getBinder();
}
}
編寫客戶端
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//獲取服務(wù)端的IBinder引用后抱虐,用來構(gòu)造Messenger
Messenger messenger = new Messenger(service);
//構(gòu)造Message
Message message = Message.obtain();
//往Message填充數(shù)據(jù)
Bundle bundle = new Bundle();
bundle.putInt("id", 100);
message.setData(bundle);
try {
//發(fā)送消息
messenger.send(message);
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void bindService() {
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
流程如下:
1昌阿、服務(wù)端構(gòu)造Handler用來接收信息
2、客戶端構(gòu)造Messenger來發(fā)送message信息
服務(wù)端收到消息打印如下:
可以看出不用編寫任何AIDL 文件就可以實(shí)現(xiàn)進(jìn)程間通信恳邀,很是方便懦冰。
2、Messenger 服務(wù)端發(fā)送消息給客戶端
上面的Demo是客戶端往服務(wù)端發(fā)送一條消息谣沸,那么如果服務(wù)端想給客戶端發(fā)送回復(fù)消息該怎么實(shí)現(xiàn)呢刷钢?
以服務(wù)端收到客戶端傳遞過來的id后,查找出id對(duì)應(yīng)的學(xué)生的姓名乳附、年齡發(fā)送給客戶端為例内地。
編寫服務(wù)端
改造一下服務(wù)端代碼:
//創(chuàng)建Handler,用來處理客戶端發(fā)送過來的消息
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Bundle bundle = msg.getData();
int id = bundle.getInt("id");
Log.d(TAG, "receive id from client, id:" + id);
//msg.replyTo 為Messenger類型赋除,從客戶端傳遞過來的
Messenger replyMessenger = msg.replyTo;
if (replyMessenger != null) {
//構(gòu)造消息
Message message = Message.obtain();
Bundle replyBundle = new Bundle();
replyBundle.putString("name", "xiaoming");
replyBundle.putInt("age", 18);
message.setData(replyBundle);
try {
replyMessenger.send(message);
} catch (Exception e) {
}
}
}
};
只是修改了handleMessage(xx)阱缓,當(dāng)收到客戶端的消息后立即返回查詢到的名字、年齡發(fā)送給客戶端举农。
編寫客戶端
客戶端代碼也僅僅需要小小的改動(dòng):
//接收服務(wù)端的信息
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Bundle bundle = msg.getData();
if (bundle != null) {
//提取姓名荆针、年齡
String name = bundle.getString("name");
int age = bundle.getInt("age");
Log.d("ipc", "receive name 、age from server, name:" + name + " age:" + age);
}
}
};
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//獲取服務(wù)端的IBinder引用后颁糟,用來構(gòu)造Messenger
Messenger messenger = new Messenger(service);
//構(gòu)造Message
Message message = Message.obtain();
//往Message填充數(shù)據(jù)
Bundle bundle = new Bundle();
bundle.putInt("id", 100);
message.setData(bundle);
//為了接收服務(wù)端的消息航背,把自己的Messenger傳遞給服務(wù)端
message.replyTo = new Messenger(handler);
try {
//發(fā)送消息
messenger.send(message);
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
客戶端在發(fā)送給服務(wù)端消息時(shí)帶上自己的Messenger以便服務(wù)端拿到該Messenger給客戶端發(fā)送信息。同時(shí)棱貌,需要重寫Handler的handleMessage(xx)接收服務(wù)端的信息玖媚。這與服務(wù)端的實(shí)現(xiàn)是一致的,相當(dāng)于雙方都有Messenger婚脱。
至此最盅,借助于Messenger突雪,輕易就完成了客戶端/服務(wù)端相互通信的功能。
3涡贱、Messenger 底層原理
從發(fā)送消息開始
為什么Messenger 能夠如此簡(jiǎn)單地完成了IPC咏删,從其發(fā)送消息開始一探究竟。
#Messenger.java
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
mTarget為IMessenger類型问词,在Messenger構(gòu)造函數(shù)里初始化:
#Messenger.java
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
而target 為Handler類型,跳轉(zhuǎn)到Handler.java里查看:
#Handler.java
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) {
msg.sendingUid = Binder.getCallingUid();
//調(diào)用Handler發(fā)送信息
Handler.this.sendMessage(msg);
}
}
看到這是不是有種似曾相似的感覺激挪,此處MessengerImpl 繼承自IMessenger.Stub,實(shí)現(xiàn)了唯一的方法:send(Message msg)垄分,該方法的的形參為:Message宛篇。
由此薄湿,我們輕易得出結(jié)論:
1叫倍、服務(wù)端對(duì)外暴露了IMessenger 接口,該接口里有唯一的方法:send(Message msg)豺瘤。
2吆倦、服務(wù)端實(shí)現(xiàn)了send(Message msg)方法,在該方法里將Message使用Handler發(fā)送出去坐求。
找到IMessenger 對(duì)應(yīng)的AIDL 文件:
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
注:該文件在framework/core/java/android/os/IMessenger.aidl
其實(shí)就是之前說的AIDL蚕泽,Message本身支持序列化,加 "in" 標(biāo)記表示數(shù)據(jù)只能從客戶端流向服務(wù)端桥嗤。
該接口定義還多了個(gè)標(biāo)識(shí):"oneway"须妻。
這個(gè)字段最終會(huì)影響IBinder.transact(xx)里的最后一個(gè)形參:
boolean _status = mRemote.transact(Stub.TRANSACTION_send,
_data, null, android.os.IBinder.FLAG_ONEWAY);
FLAG_ONEWAY 表示transact(xx)不是阻塞調(diào)用,也就是說客戶端調(diào)用該方法后立即返回泛领,不等待璧南。仔細(xì)想想其實(shí)也并不用等待,因?yàn)閟end(xx)沒有返回值师逸,又是in 修飾形參司倚,數(shù)據(jù)流不能從服務(wù)端流入客戶端。
與普通的AIDL 相比篓像,Messenger其實(shí)就是封裝了服務(wù)端的接口及其方法动知,與此同時(shí)也封裝了客戶端調(diào)用服務(wù)端的方法≡北纾客戶端將要傳遞的消息封裝在Message里盒粮,通過Messenger傳遞出去,服務(wù)端收到后在Handler里處理該Message奠滑。
服務(wù)端為什么能夠向客戶端發(fā)送消息
從IMessenger.aidl定義可知丹皱,通過send(xx)方法只能是由客戶端往服務(wù)端發(fā)送消息妒穴,并且沒有返回值。因此想通過方法的形參或返回值攜帶服務(wù)端的數(shù)據(jù)是不現(xiàn)實(shí)的了摊崭。
客戶端能給服務(wù)端發(fā)送數(shù)據(jù)的最大憑證是:客戶端能夠拿到服務(wù)端的IBinder接口讼油。那么想想反過來行嗎?
來看看Message.java里的字段:
#Message.java
/**
* Optional Messenger where replies to this message can be sent. The
* semantics of exactly how this is used are up to the sender and
* receiver.
*/
public Messenger replyTo;
Message可以持有Messenger 引用呢簸,而我們知道構(gòu)造好了Messenger矮台,就可以通過getBinder(xx)獲取關(guān)聯(lián)的IBinder 引用。
#Messenger.java
public IBinder getBinder() {
return mTarget.asBinder();
}
IBinder有了剩下的就是想辦法把它傳給服務(wù)端根时。
客戶端傳遞IBinder給服務(wù)端
Message需要跨進(jìn)程傳遞瘦赫,因此它的成員變量包括Messenger replyTo 都需要序列化及反序列化。
#Message.java
public void writeToParcel(Parcel dest, int flags) {
...
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
...
}
調(diào)用了Messenger里的靜態(tài)方法:
#Messenger.java
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
//獲取messenger 關(guān)聯(lián)的Binder确虱,寫入到序列化對(duì)象
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);
}
至此替裆,客戶端的IBinder引用就可以傳遞給服務(wù)端了。
服務(wù)端取出IBinder
服務(wù)端收到Message后反序列化成員變量如Messenger replyTo等。
#Message.java
private void readFromParcel(Parcel source) {
...
replyTo = Messenger.readMessengerOrNullFromParcel(source);
...
}
類似的調(diào)用Messenger 靜態(tài)方法:
#Messenger.java
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
//反序列化出IBinder
IBinder b = in.readStrongBinder();
//構(gòu)造出Messenger
return b != null ? new Messenger(b) : null;
}
最終南缓,服務(wù)端收到了客戶端的IBinder,并構(gòu)造出Messenger纸镊,有了Messenger當(dāng)然可以給客戶端發(fā)消息了概疆。
4、Message岔冀、AIDL、Messenger區(qū)別與聯(lián)系
Message
用來在線程間傳遞數(shù)據(jù)罐呼,與Handler配合使用侦高,本身支持序列化,可以跨進(jìn)程傳遞Message對(duì)象计螺。
AIDL
用來簡(jiǎn)化進(jìn)程間通信時(shí)客戶端、服務(wù)端代碼的編寫登馒。
Messenger
在AIDL 的基礎(chǔ)上,進(jìn)一步封裝服務(wù)端暴露的接口肺孤,將服務(wù)端收到Message通過Handlder發(fā)送到目標(biāo)線程济欢。
AIDL 與 Messenger 在進(jìn)程間通信區(qū)別:
使用AIDL優(yōu)點(diǎn):
1、可以靈活的編寫服務(wù)端的接口茫叭,并且能夠自定義方法形參類型半等、數(shù)據(jù)流向、方法返回值莽囤。
2、服務(wù)端方法實(shí)現(xiàn)里可以開啟多線程處理數(shù)據(jù)切距。
使用AIDL 缺點(diǎn):
1、需要編寫AIDL 文件定義服務(wù)端接口话肖。
2葡幸、如果是自定義數(shù)據(jù)類型,還需要編寫對(duì)應(yīng)的AIDL 文件床蜘。
使用Messenger 優(yōu)點(diǎn):
1蔑水、無需定義AIDL 文件,直接構(gòu)造Message發(fā)送弹囚。
2领曼、快速實(shí)現(xiàn)雙向通信(嚴(yán)格上來說AIDL也能實(shí)現(xiàn)蛮穿,只是Messenger封裝好了IBinder的傳遞)
使用Messenger缺點(diǎn):
參考上方践磅,AIDL 優(yōu)點(diǎn)即是Messenger缺點(diǎn)灸异。
適用場(chǎng)合
如果是簡(jiǎn)單的通信,并且服務(wù)端是單線程順序處理客戶端的消息肺樟,建議使用Messenger。
本文基于Android 10.0疟暖。
您若喜歡田柔,請(qǐng)點(diǎn)贊、關(guān)注欣舵,您的鼓勵(lì)是我前進(jìn)的動(dòng)力
持續(xù)更新中缀磕,和我一起步步為營學(xué)習(xí)Android
1、Android各種Context的前世今生
2准验、Android DecorView 必知必會(huì)
3廷没、Window/WindowManager 不可不知之事
4垂寥、View Measure/Layout/Draw 真明白了
5、Android事件分發(fā)全套服務(wù)
6滞项、Android invalidate/postInvalidate/requestLayout 徹底厘清
7、Android Window 如何確定大小/onMeasure()多次執(zhí)行原因
8过椎、Android事件驅(qū)動(dòng)Handler-Message-Looper解析
9戏仓、Android 鍵盤一招搞定
10亡鼠、Android 各種坐標(biāo)徹底明了
11敷待、Android Activity/Window/View 的background
12、Android Activity創(chuàng)建到View的顯示過
13勾哩、Android IPC 系列
14举哟、Android 存儲(chǔ)系列
15、Java 并發(fā)系列不再疑惑
16炎滞、Java 線程池系列