前言
Messenger中文意思是送信者,通過Messenger我們可以在不同程式之間傳遞Message物件,其底層也是通過上文講到的AIDL實現的,先來看看基本用法
一、基本用法
以客戶端傳送訊息給服務端,服務端回覆一個訊息給客戶端為例。
// 執行在其它程式
class MessengerService : Service() {
class MessengerHandler : Handler() {
val TAG = "MessengerHandler"
override fun handleMessage(msg: Message?) {
when (msg?.what) {
ADD_BOOK -> {
// 必須設定classLoader不然會丟擲異常
msg.data.classLoader = Thread.currentThread().contextClassLoader
Log.d(TAG, "do add book ${msg.data.get("book")}")
val replyMessage = Message.obtain(null, REPLY_ADD_BOOK)
val bundle = Bundle()
bundle.putString("reply", "我收到了響應")
replyMessage.data = bundle
msg.replyTo.send(replyMessage)
}
ALL_BOOKS -> {
Log.d(TAG, "do all books")
}
}
}
}
override fun onBind(intent: Intent?): IBinder? {
val messenger = Messenger(MessengerHandler())
return messenger.binder
}
}
複製程式碼
接著在MainActivity中繫結服務,程式碼如下
class MainActivity : AppCompatActivity() {
private lateinit var connection: ServiceConnection
private lateinit var replyMessenger: Messenger
private var messenger: Messenger? = null
class GetReplyHandler : Handler() {
private var TAG = "MainActivity"
override fun handleMessage(msg: Message?) {
when (msg?.what) {
REPLY_ADD_BOOK -> {
Log.d(TAG, msg.data.getString("reply"))
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
messenger = Messenger(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
replyMessenger = Messenger(GetReplyHandler())
val intent = Intent(this, MessengerService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
fun send(v: View) {
val msg = Message.obtain(null, ADD_BOOK)
// msg.obj = Book(0, "第0本書") 不能使用obj跨程式傳遞自定義的Parcelable物件,使用Bundle因為其可以設定classLoader
val msg = Message.obtain(null, ADD_BOOK)
val bundle = Bundle()
bundle.putParcelable("book", Book(0, "第0本書"))
msg.data = bundle
msg.replyTo = replyMessenger
messenger?.send(msg)
}
override fun destroy() {
unbindService(connection)
}
}
複製程式碼
注意send方法內部傳送的Parcelable類(這裡是Book)在服務端必須也要存在,這樣當呼叫MainActivity的send方法時就會服務程式就會列印出do add book,下面就來看看該過程的原始碼
二、原始碼分析
- 客戶端傳送訊息給服務端
首先我們在MessengerService的onBind中建立了一個Messenger例項,所以我們就從Messenger構造器開始說起
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
複製程式碼
繼續看看Handler的getIMessager
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
複製程式碼
剛開始mMessenger肯定為null,繼續看看MessengerImpl
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
複製程式碼
這裡居然有個Stub!瞬間想起了AIDL,於是就去找了找有沒有IMessenger.aidl這個檔案,最終找到了該檔案,檔案內容如下
oneway interface IMessenger {
void send(in Message msg);
}
複製程式碼
這裡的oneway表示呼叫send方法並不會掛起當前執行緒等待服務端執行,而是會立即返回,send方法實現為將收到的訊息傳送給建立Messenger時的入參,至此服務端的Messenger使用分析完畢接著看看客戶端中Messenger的使用,在onServiceConnected中通過傳入的Binder物件構造了Messenger物件。
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
複製程式碼
呼叫asInterface(上篇文章有講到)拿到IMessenger.Stub.Proxy例項賦值給mTarget,最後客戶端通過呼叫Messengr.send傳送訊息
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
複製程式碼
不管這裡send是個直接呼叫還是IPC呼叫都會呼叫到以下MessengerImpl的send方法,該方法又把訊息傳送到了對應的Handler,因此服務端的Handler就能收到訊息了
- 服務端收到訊息回覆客戶端
注意客戶端將replyMessenger設定給了Message.replyTo然後傳送訊息,這個過程中會呼叫Message.writeToParcel
public void writeToParcel(Parcel dest, int flags) {
if (callback != null) {
throw new RuntimeException(
"Can't marshal callbacks across processes.");
}
dest.writeInt(what);
dest.writeInt(arg1);
dest.writeInt(arg2);
if (obj != null) {
try {
Parcelable p = (Parcelable)obj;
dest.writeInt(1);
dest.writeParcelable(p, flags);
} catch (ClassCastException e) {
throw new RuntimeException(
"Can't marshal non-Parcelable objects across processes.");
}
} else {
dest.writeInt(0);
}
dest.writeLong(when);
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
}
複製程式碼
又會呼叫到Messenger.writeMessengerOrNullToParcel
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);
}
複製程式碼
向Parcel中寫入了一個MessengerImpl例項(Binder),然後在IPC結束後會呼叫Message.readFromParcel
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
}
複製程式碼
又呼叫到了Messenger.readMessengerOrNullFromParcel
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}
複製程式碼
如果是跨程式傳遞那麼讀取的會是一個BinderProxy物件,通過該BinderProxy構造Messenger物件其內部的mTarget就會是一個IMessenger.Stub.Proxy例項,因此服務端就可以呼叫客戶端的對應方法了