在上幾篇文章中,我們已經學習了Binder 這種通信模式,這章中,我們來學習 Messenger;
Messenger可以翻譯爲信使,它可以在不同進程間傳遞 Message 對象。Messenger 是一種輕量的 IPC 防範,它的底層實現是 AIDL,後面我們會分析它的源碼;
Messenger 一次只能處理一個請求,因此並不適合高併發的問題,也不用考慮線程同步的問題,因爲服務端中不存在併發執行的情形。
一. 實現Messenger通信
從上面可以知道,可以傳遞 Message,一些簡單的數據,我們就可以通過這種方式去實現了。下面我們一起來實現它。
今天的效果是,客戶端發送兩個數字,服務端返回兩數之和:
1.1. 服務端
在服務端中,我們可以直接 new 一個 Messenger ,傳遞一個 Handler,如下:
public class MessengerService extends Service {
private static final String TAG = "MessageService";
public MessengerService() {
}
Messenger mMessenger = new Messenger(new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
});
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
可以看到,Messenger 的構造方法中,傳遞了一個 Handler,當接收到客戶端的 Message 消息時,會在 handleMessage() 方法中處理,並通過 mMessenger.getBinder() 返回 binder 對象。
接着,需要通過 Message 拿到兩個數字,並返回相加的結果,如下:
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
try {
int num = msg.arg1 + msg.arg2;
Message message = Message.obtain();
message.what = 2;
message.arg1 = num;
//通過客戶端的 Messenger 來把數據發送回去
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
需要注意的是,數據在返回的時候,需要通過 msg.replyTo 去發送,它的對象爲 Messenger 。
這樣,服務端就寫好了。
1.2. 客戶端
接着,我們新建一個moudle,通過包名加類名的方式去 bind 這個服務。如下:
Intent intent = new Intent();
//綁定 服務
intent.setClassName("com.example.ipcdemo","com.example.ipcdemo.service.MessengerService");
mRemoteService = new RemoteService();
bindService(intent,mRemoteService, Service.BIND_AUTO_CREATE);
在連接的時候,創建 Messenger :
class RemoteService implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinder = new Messenger(service);
mSb.append("連接上服務端").append("\n");
mTextView.setText(mSb.toString());
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBinder = null;
}
}
這樣,我們就拿到了服務端mBinder的實例了,然後通過點擊事件,把數據發送過去:
public void testBtn(View view) {
Message msg = Message.obtain();
msg.arg1 = (int) (Math.random() * 100+1);
msg.arg2 = (int) (Math.random() * 100+1);
mTextView.setText(mSb.toString());
//傳遞 client 的 messenger ,實現服務端與客戶端通信
msg.replyTo = mMessenger;
if (mBinder != null) {
try {
mBinder.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
同時,也要注意,把要接受服務端信息的 Messenger 綁定到 msg.replyTo 中。客戶端的 Messenger 如下:
Messenger mMessenger = new Messenger(new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
mSb.append(msg.arg1).append("\n");
mTextView.setText(mSb.toString());
}
});
這樣,我們就實現所有功能了。效果如下面的 gif 。
使用 Messenger 的優缺點如下:
優點 | 缺點 | 使用場景 |
---|---|---|
功能一般,支持一對多穿行通信,支持實時通信 | 不能很好的處理高併發情形,不支持RPC,數據通過Message傳輸,因此只能傳輸Bundle支持的數據類型,比如 msg.setData() | 低併發的一對多及即時通信,無RPC需求,或者無需要返回結果的RPC需求 |
二. 源碼分析
上面說到,Messenger 是基於 AIDL 的,怎麼得出來的呢?它的構造方法傳遞要求傳入的 Handler,進入到裏面看一下:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
然後,去到 Handler ,找到 getIMessage() 方法:
可以看到,mMessenger 的實現類爲 MessengerImpl ,而它繼承自 IMessenger.Stub ;看到這裏有沒有很熟悉? 沒錯,跟我們的 AIDL 的實現類一樣,通過源碼也知道,IMessenger 確實是個 AIDL 接口:
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
在它的 send 方法中,把數據又通過 Handler 的 sendMessage 方法,發送給自身,從而當有數據來的時候,就會調用它的 handleMessage()方法。
而在客戶端的綁定數據中,也驗證了這個問題,比如:
去到構造方法中,可以看到:
通過 asInterface 拿到binder。
這樣,我們就瞭解清楚了。