Android 進階——輕量級跨程序傳遞Message利器Messenger詳解
引言
作為Android 開發者相信我們對於訊息機制一定非常熟悉,對於程序內使用Handler處理Message 也一定了如執掌,而如果讓你使用最簡潔的方式實現程序間通訊,也許有相當一部分初學者想到的是用AIDL自己實現,誠然思路是對的,但是還有更簡單的機制供你使用。
一、Messenger 概述
Messenger是基於訊息Message的傳遞的一種輕量級IPC程序間通訊方式(通過在一個程序中建立一個指向Handler的Messenger,並將該Messenger傳遞給另一個程序),當然本質就是對Binder的封裝(也是通過AIDL實現的 )。通過Messenger可以讓我們可以簡單地在程序間直接使用Handler進行Message傳遞,跨程序是通過Binder(AIDL實現),而訊息傳送是通過Handler#sendMessage方法,而處理則是Handler#handleMessage處理的;當然除了Handler之外還可以是自定義的相關的某些IBinder介面,簡而言之,Messenger的跨程序能力是由構造時關聯的物件提供的。
二、Messenger 原始碼解析
Messenger 實現了Parcelable介面,意味著自身可以跨程序傳遞,同時持有IMessenger 介面引用(一個Binder物件)意味著拿到這個Binder物件就可以跨程序使用。Messenger 只是把IMessenger介面包裝起來並通過Binder進行跨程序傳遞,真正的核心能力提供者是IMessenger的實現類——android.os.Handler.MessengerImpl。
package android.os; /** * Reference to a Handler, which others can use to send messages to it. * This allows for the implementation of message-based communication across * processes, by creating a Messenger pointing to a Handler in one process, * and handing that Messenger to another process. */ public final class Messenger implements Parcelable { private final IMessenger mTarget; /** * Create a new Messenger pointing to the given Handler. Any Message * objects sent through this Messenger will appear in the Handler as if * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had been called directly. * * @param target The Handler that will receive sent messages. */ public Messenger(Handler target) { mTarget = target.getIMessenger(); } /** * Create a Messenger from a raw IBinder, which had previously been retrieved with {@link #getBinder}. * @param target The IBinder this Messenger should communicate with. */ public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); } /** * Send a Message to this Messenger's Handler. * * @param message The Message to send. Usually retrieved through * {@link Message#obtain() Message.obtain()}. */ public void send(Message message) throws RemoteException { mTarget.send(message); } /** * Retrieve the IBinder that this Messenger is using to communicate with * its associated Handler. * @return Returns the IBinder backing this Messenger. */ public IBinder getBinder() { return mTarget.asBinder(); } public boolean equals(Object otherObj) { if (otherObj == null) { return false; } try { return mTarget.asBinder().equals(((Messenger)otherObj) .mTarget.asBinder()); } catch (ClassCastException e) { } return false; } public int hashCode() { return mTarget.asBinder().hashCode(); } public int describeContents() { return 0; } public void writeToParcel(Parcel out, int flags) { out.writeStrongBinder(mTarget.asBinder()); } public static final Parcelable.Creator<Messenger> CREATOR = new Parcelable.Creator<Messenger>() { public Messenger createFromParcel(Parcel in) { IBinder target = in.readStrongBinder(); return target != null ? new Messenger(target) : null; } public Messenger[] newArray(int size) { return new Messenger[size]; } }; public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) { out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() : null); } public static Messenger readMessengerOrNullFromParcel(Parcel in) { IBinder b = in.readStrongBinder(); return b != null ? new Messenger(b) : null; } }
1、IMessenger介面
IMessenger是通過AIDL 自動生成的,一般在原生Android系統中I字首的都是AIDL介面對應的實現類。對應的Messenger.aidl:
package android.os;
parcelable Messenger;
而IMessenger.aidl裡就定義了一個入參為Message的方法 send(in Message msg)
package android.os; import android.os.Message; /** @hide */ oneway interface IMessenger { void send(in Message msg); }
Messenger.aidl對應的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就是一個Binder 介面只提供了一個方法——send 用於跨程序傳送訊息Message。
2、Messenger 主要方法
2.1、Messenger(Handler target)
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
通過Handler構造Messenger時,就是呼叫了傳入的Handler#getIMessenger()方法得到單例構造的MessengerImpl例項並初始化mTarget。
//Handler#getIMessenger()
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();
Handler.this.sendMessage(msg);
}
}
本質就是通過Handler#sendMessage完成通訊。
2.2、Messenger(IBinder target)
而通過IBinder物件構造Messenger時,就是把傳入的IBinder物件“轉成”MessengerImpl例項並初始化mTarget成員變數。
並非簡單地直接強轉而是先檢索,如果已經建立過了就直接返回IBinder對應的代理物件,否則建立對應的代理物件再返回,預知詳情請後續關注Binder系列文章
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
2.3、send(Message message)
前面分析了send方法本質就是呼叫Handler#sendMessage方法,這也解釋了為什麼我們在服務端和客戶端都需要建立Handler,因為需要在Handler去處理接收到的訊息。
public void send(Message message) throws RemoteException {
mTarget.send(message);//MessengerImpl#send
}
三、Messenger的使用
Messenger 基於Binder可以跨程序通訊,為了方便我簡單的把一個程序稱之為服務端程序,另一個稱之為客戶端程序
1、首先在服務端定義一個Messenger物件
Messager.replyTo指向的客戶端的Messenger,而Messenger又持有客戶端的一個IBinder物件(即MessengerImpl),服務端正是利用這個IBinder物件做的與客戶端的通訊。
- 建立一個Handler
- 使用Handler初始化構建Messenger
package com.crazymo.messenger.rawmessenger;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
public class MessengerService extends Service {
public final static String TAG = "MessengerIPC";
public final static String KEY_NAME = "Name";
public final static String KEY_RESP = "Response";
public final static int MSG_WHAT_HELLO = 100;
public final static int MSG_WHAT_RESP = 1001;
/**
* 一個用於跨程序的序列化物件,包裹著IMessenger AIDL介面
*/
private static final Messenger messenger = new Messenger(new MessengerServerHandler());
private static class MessengerServerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
doHandleMessage(msg);
}
}
/**
* 處理其他程序發過來的訊息
* @param msg
*/
private static void doHandleMessage(Message msg) {
if (msg != null) {
String ret = "hello ";
//接收客戶端的訊息並處理
if (msg.what == MSG_WHAT_HELLO) {
Log.e(TAG, "receive msg from client=" + msg.getData().getString(KEY_NAME));
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ret += msg.getData().getString(KEY_NAME);
//把處理結果封裝到Message返回給客戶端
Message reply = Message.obtain(null, MSG_WHAT_RESP);
Bundle bundle = new Bundle();
bundle.putString(KEY_RESP, ret);
reply.setData(bundle);
try {
//msg.replyTo @ android.os.Messenger型別,Messager.replyTo指向的客戶端的Messenger,而Messenger又持有客戶端的一個Binder物件(MessengerImpl)。服務端正是利用這個Binder物件做的與客戶端的通訊。
if (msg.replyTo != null) {
msg.replyTo.send(reply);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
} else {
Log.e(TAG, "handle client empty msg");
}
}
@Override
public IBinder onBind(Intent intent) {
//返回Messenger的IBinder物件,當bindService 執行時候就會觸發該回調,就可以拿到服務端的IBinder物件
return messenger.getBinder();
}
}
然後就在傳入Handler#handleMessage 方法中實現處理訊息的邏輯,至此一個遠端Service實現完畢。
2、客戶端使用Messenger
- 定義一個Handler 用於傳送Message
- 初始化Messenger物件
package com.crazymo.messenger;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.crazymo.messenger.aidl.MyMessengerService;
import com.crazymo.messenger.rawmessenger.MessengerService;
public class MainActivity extends AppCompatActivity {
private final static String TAG="MainActivity";
/***********************1、Messenger 方式****************************/
private Messenger mServer;
private ServiceConnection connMessenger =new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServer=new Messenger(service);//把返回的IBinder物件初始化Messenger
Log.e(MessengerService.TAG, "MessengerService Connected!");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private final Handler handlerClient =new Handler(){
@SuppressLint("HandlerLeak")
@Override
public void handleMessage(Message msg) {
if(msg!=null && msg.what== MessengerService.MSG_WHAT_RESP){
String resp=msg.getData().getString(MessengerService.KEY_RESP);
Log.e(MessengerService.TAG, "resp from server="+resp);
}
}
};
//為了接收服務端的回覆,客戶端也需要準備一個接收訊息的Messenger 和Handler
private final Messenger clientMessenger=new Messenger(handlerClient);
private void bindMessengerService() {
Intent intent=new Intent(this,MessengerService.class);
bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);
}
public void sendByMessenger(View view) {
Message msg=Message.obtain(null,MessengerService.MSG_WHAT_HELLO);
Bundle data=new Bundle();
data.putString(MessengerService.KEY_NAME,"CrazyMo_");
msg.setData(data);
//Client 發信時指定希望回信人,把客戶端程序的Messenger物件設定到Message中
msg.replyTo=clientMessenger;
try {
mServer.send(msg);//跨程序傳遞
} catch (RemoteException e) {
e.printStackTrace();
}
}
/***********************2、MyMessenger AIDL方式****************************/
private IMyMessenger myInterface;
private ServiceConnection connAidl = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myInterface = IMyMessenger.Stub.asInterface(service);
Log.i(TAG, "MyMessenger 連線Service 成功");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "連線Service失敗");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindMessengerService();
bindMyMessengerService();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connMessenger);
unbindService(connAidl);
}
public void sendByMyMessenger(View view) {
try {
String ret=myInterface.send("hello server my MyMessenger");
Log.i(TAG, "myInterface.send的結果="+ret);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void bindMyMessengerService() {
Intent intent=new Intent(this, MyMessengerService.class);
bindService(intent, connAidl, Context.BIND_AUTO_CREATE);
}
}
3、傳統AIDL實現 VS Messenger
// IMyMessenger.aidl
package com.crazymo.messenger;
interface IMyMessenger {
String send( String aString);
}
public class MyMessengerService extends Service {
private static final String TAG="MyMessengerService";
private MyMessengerBinder mBinder=new MyMessengerBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private static class MyMessengerBinder extends IMyMessenger.Stub{
@Override
public String send(String aString) throws RemoteException {
String ret="reply to client"+aString;
Log.e(TAG,"received str from c:"+aString);
return ret;
}
}
}
兩者對比你會發現以原程服務的形式使用傳統AIDL和Messenger大同小異,區別僅僅是在於初始化的時候,當onServiceConnected方法回撥時初始化構造遠端Binder物件的方式有所差別,剩下的基本一模一樣。