【Android】跨程序通訊AIDL和messenger詳解
1.定義
我們都知道,Android應用一旦啟動就會生成一個以包名為名字的程序,當然我們還知道android中很多manager都是執行在system server程序中的,像AMS,PMS,WMS等,它們都是通過binder來程序遠端呼叫,說到這就不得不說多程序之間的通訊問題,程序不像執行緒那樣,多執行緒可以共享記憶體,而每個程序的記憶體都是獨立的,無法直接訪問,因此安卓提供了binder來進行程序間的通訊。
在使用binder的過程中,android定義了一些更方便的程序間通訊的方式,像AIDL,Messenger等方式,今天我們不討論binder的原理,先來學習一下AIDL和Messenger的基本使用,廢話不多說,咱們切入正題。
2.AIDL在Android中的使用
AIDL的英文名叫做:Android Interface Definition Language,意思就是Android介面定義語言,它是用於定義伺服器和客戶端通訊介面的一種描述語言,可以用它來生成IPC的程式碼,那麼什麼時候我們才需要用到AIDL呢,雖然表面上來說是程序間通訊就可以用,但是上面我們還提到了Messenger,它們分別適合什麼場景呢?
只有允許不同應用的客戶端用 IPC 方式訪問服務,並且想要在服務中處理多執行緒時,才有必要使用 AIDL。如果不需要執行跨越不同應用的併發IPC,就應該通過實現一個Binder建立介面;或者如果想執行IPC,但根本不需要處理多執行緒,則使用Messenger類來實現介面。
下面我們來看下AIDL的使用方式:
服務端程式碼
首先我們定義一個場景,比如我們想獲取遠端程序的school名字和school列表,那我們就可以在遠端程序中也就是我們所說的服務端定義aidl檔案,首先我們先定一個School實體類:
public class School implements Parcelable { private String schoolName; private String address; public School(String schoolName, String address) { this.schoolName = schoolName; this.address = address; } protected School(Parcel in) { schoolName = in.readString(); address = in.readString(); } public static final Creator<School> CREATOR = new Creator<School>() { @Override public School createFromParcel(Parcel in) { return new School(in); } @Override public School[] newArray(int size) { return new School[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(schoolName); dest.writeString(address); } public void readFromParcel(Parcel dest) { schoolName=dest.readString(); address=dest.readString(); } public String getSchoolName() { return schoolName; } public void setSchoolName(String schoolName) { this.schoolName = schoolName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
注意這裡一定要實現Parcelable介面,因為aidl傳遞物件要求必須要序列化物件,android studio工具不會提示readFromParcel方法,我們自己記得加上,然後我們給物件定義一個aidl檔案,即School.aidl:
// ISchool.aidl package com.wangkeke.aidldemo; import com.wangkeke.aidldemo.School; // Declare any non-default types here with import statements parcelable School;
注意這裡的parcelable首字母要小寫,import需要把School類匯入,然後我們再定義上面所說的獲取學校名字,列表等功能的aidl檔案,即SchoolControllerAIDL.aidl:
package com.wangkeke.aidldemo; import com.wangkeke.aidldemo.School; interface SchoolControllerAIDL { String getSchoolName(String schoolNo); List<School> getSchools(); }
同樣要注意匯入相應的包,接下來我們clean一下專案,就會在如下目錄下生成對應的SchoolControllerAIDL類:

自動生成的檔案
aidl檔案建立好了,當要想和遠端程序互動,還需要藉助service來bind遠端服務,下面我們建立一個AidlService:
public class AidlService extends Service { private List<School> getSchoolLists(){ List<School> schoolList = new ArrayList<>(); schoolList.add(new School("清華大學","北京")); schoolList.add(new School("北京大學","北京")); schoolList.add(new School("南京大學","南京")); schoolList.add(new School("上海復旦大學","上海")); return schoolList; } SchoolControllerAIDL.Stub mBinder = new SchoolControllerAIDL.Stub() { @Override public String getSchoolName(String schoolNo) throws RemoteException { return "伺服器端的schoolname:清華大學"; } @Override public List<School> getSchools() throws RemoteException { return getSchoolLists(); } }; @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } }
程式碼很簡單,就是通過上面自動生成的SchoolControllerAIDL類的Stub內部類來建立一個binder物件,把它作為service中onBind方法的返回值即可,記得在AndroidManifest檔案中定義:
<service android:name=".AidlService" />
客戶端程式碼
下面我們就可以編寫客戶端的程式碼了,我們都知道bindService需要傳遞一個ServiceConnection物件,我們來定義一下:
private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isConnection = true; schoolAIDL = SchoolControllerAIDL.Stub.asInterface(service); try { schoolAIDL.registerListener(lisener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { isConnection = false; } };
然後在onCreate中bind服務:
Intent intent = new Intent(this,AidlService.class); bindService(intent, mConnection ,Context.BIND_AUTO_CREATE);
一旦執行上面的bindService程式碼,ServiceConnection中的onServiceConnected方法就會被呼叫,此時表明ServiceConnection已經連線成功,客戶端可以訪問服務端的方法了,現在我們呼叫一下之前獲取學校和學校列表的方法試試:
btnRemote.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(null != schoolAIDL){ try { String schoolName = schoolAIDL.getSchoolName("STU1002"); Toast.makeText(ThirdProcessActivity.this, ""+schoolName, Toast.LENGTH_SHORT).show(); List<School> listSchools = schoolAIDL.getSchools(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < listSchools.size(); i++) { sb.append("學校名字:"+listSchools.get(i).getSchoolName()+"學校地址:"+listSchools.get(i).getAddress()+"\n"); } tvShow.setText(sb.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } });
執行結果如下:

aidl執行結果
<activity android:name=".ThirdProcessActivity" android:process=":other" />
另外還要注意如果新啟動的activity執行在新程序中,那麼Application會在新啟動Activity的適合再次呼叫,我們來驗證一下這個結論,修改下application中的程式碼:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); int pid = android.os.Process.myPid(); Log.e("wangkeke", "MyApplication is oncreate====="+"pid="+pid); String processNameString = ""; ActivityManager mActivityManager = (ActivityManager)this.getSystemService(getApplicationContext().ACTIVITY_SERVICE); for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) { if (appProcess.pid == pid) { processNameString = appProcess.processName; } } if("com.wangkeke.aidldemo".equals(processNameString)){ Log.e("wangkeke", "當前程序為:"+processNameString+"-----主程序(服務端程序)"); }else{ Log.e("wangkeke", "當前程序為:"+processNameString+"-----clent程序(客戶端程序)"); } } }
然後我們執行並且跳轉新程序activity,結果如下:

application列印日誌結果
正如我們預料的那樣,application確實執行了兩次,不過解決方式也正如上面列印的日誌那樣,根據程序名判斷當前執行的是哪一個Application,進行相應的處理即可。
插播一條:服務端和客戶端進行遠端通訊,傳遞的資料必須是aidl支援的,aidl支援如下資料型別:基本資料型別,String,CharSequence,List,Map(集合中的所有元素必須是aidl支援的型別),實現了Parcelable介面的物件。
雖然我們上面實現了客戶端和服務端的通訊,但是大家可能也發現了,這tm也太簡單了吧,呼叫個方法就完事了,我客戶端想要回調怎麼辦呢,各位彆著急,android自然也是提供了客戶端回撥的處理類,RemoteCallbackList類,在AIDL中客戶端向服務端註冊一個回撥方法時,服務端要考慮客戶端是否意外退出(客戶端因為錯誤應用Crash,或者被Kill掉了),服務端還不知道,此時去回撥客戶端,出現錯誤。
那麼該如何使用呢,其實也很簡單,我們在上面的例子中稍微改動下,首先我們在服務端定義一個OnConnectSuccessLisener.aidl,它用來告知服務端的連線狀態,並提供一個像客戶端傳送資料的方法:
// OnConnectSuccessLisener.aidl package com.wangkeke.aidldemo; // Declare any non-default types here with import statements interface OnConnectSuccessLisener { void onServiceConnected(); void onServiceDisConnected(); void sendMsgToClient(String msg); }
然後我們修改下SchoolControllerAIDL.aidl類,增加註冊回撥和反註冊回撥的方法:
// SchoolControllerAIDL.aidl package com.wangkeke.aidldemo; import com.wangkeke.aidldemo.School; import com.wangkeke.aidldemo.OnConnectSuccessLisener; // Declare any non-default types here with import statements interface SchoolControllerAIDL { String getSchoolName(String schoolNo); List<School> getSchools(); void registerListener(OnConnectSuccessLisener listener); void unregisterListener(OnConnectSuccessLisener listener); }
服務端的AidlService也要稍作改動:
public class AidlService extends Service { private RemoteCallbackList<OnConnectSuccessLisener> mListener = new RemoteCallbackList<>(); private List<School> getSchoolLists(){ List<School> schoolList = new ArrayList<>(); schoolList.add(new School("清華大學","北京")); schoolList.add(new School("北京大學","北京")); schoolList.add(new School("南京大學","南京")); schoolList.add(new School("上海復旦大學","上海")); return schoolList; } SchoolControllerAIDL.Stub mBinder = new SchoolControllerAIDL.Stub() { @Override public String getSchoolName(String schoolNo) throws RemoteException { return "伺服器端的schoolname:清華大學"; } @Override public List<School> getSchools() throws RemoteException { return getSchoolLists(); } @Override public void registerListener(OnConnectSuccessLisener listener) throws RemoteException { listener.onServiceConnected(); mListener.register(listener); checkCameraState(); } @Override public void unregisterListener(OnConnectSuccessLisener listener) throws RemoteException { listener.onServiceDisConnected(); mListener.unregister(listener); } }; private void checkCameraState() { new Thread(new Runnable() { @Override public void run() { try { //模擬耗時任務 Thread.sleep(3000); final int N = mListener.beginBroadcast(); for (int i = 0; i < N; i++) { OnConnectSuccessLisener successLisener = mListener.getBroadcastItem(i); successLisener.sendMsgToClient("我是服務端處理完耗時任務後,給客戶端傳送的值"); } mListener.finishBroadcast(); } catch (InterruptedException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }).start(); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } }
客戶端程式碼如下:
/** * 我是客戶端 */ public class ThirdProcessActivity extends AppCompatActivity { private boolean isConnection = false; private Button btnRemote; private TextView tvShow; OnConnectSuccessLisener lisener = new OnConnectSuccessLisener.Stub() { @Override public void onServiceConnected() throws RemoteException { Log.e("wangkeke","連線成功"); } @Override public void onServiceDisConnected() throws RemoteException { Log.e("wangkeke","斷開連線"); } @Override public void sendMsgToClient(String msg) throws RemoteException { Log.e("wangkeke","客戶端收到訊息:"+msg); } }; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isConnection = true; schoolAIDL = SchoolControllerAIDL.Stub.asInterface(service); try { schoolAIDL.registerListener(lisener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { isConnection = false; } }; private SchoolControllerAIDL schoolAIDL; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_third_process); Intent intent = new Intent(this,AidlService.class); bindService(intent, mConnection ,Context.BIND_AUTO_CREATE); btnRemote = findViewById(R.id.btn_remote); tvShow = findViewById(R.id.tv_show); btnRemote.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(null != schoolAIDL){ try { String schoolName = schoolAIDL.getSchoolName("STU1002"); Toast.makeText(ThirdProcessActivity.this, ""+schoolName, Toast.LENGTH_SHORT).show(); List<School> listSchools = schoolAIDL.getSchools(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < listSchools.size(); i++) { sb.append("學校名字:"+listSchools.get(i).getSchoolName()+"學校地址:"+listSchools.get(i).getAddress()+"\n"); } tvShow.setText(sb.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } }); } @Override protected void onDestroy() { super.onDestroy(); try { if(isConnection && schoolAIDL.asBinder().isBinderAlive()){ schoolAIDL.unregisterListener(lisener); } } catch (RemoteException e) { e.printStackTrace(); } unbindService(mConnection); } }
我們在bindService之後,onServiceConnected建立連線,通過registerListener方法客戶端註冊了回撥方法,此時服務端接收到後會處理一個耗時任務,然後通過RemoteCallbackList的register方法,把listener儲存起來,此時我們執行,結果如下:

客戶端註冊回撥
可以看到我們在啟動客戶端之後,3秒後會自動回撥給客戶端資料,其實到這裡我們好像就可以通過儲存registerListener(OnConnectSuccessLisener listener)的引數OnConnectSuccessLisener,可以在服務端任何地方給客戶端發訊息,前提是客戶端還在執行,比如把定義一個static型別的變數把listener存起來,不過這種方式很不優雅,不知道大家有好的方式沒,歡迎留言討論(當然也可以通過客戶端不斷的輪訓來獲取服務端的某個狀態後再返回需要的資料)。
注意:RemoteCallbackList是用於專門新增和刪除跨程序listener的類,它內部維護了一個mCallbacks,以客戶端和服務端建立的IBinder為key,所以新增和移除callback變得簡單明瞭:
ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
我們通過它的register和unregister方法來實時新增和刪除callback,保證了回撥的正確性!
另外,無論是客戶端訪問服務端的方法還是服務端訪問客戶端,發起方在已知知對方是耗時操作的情況下,一定不要在主執行緒發起呼叫,因為發起方發起遠端請求之後,當前執行緒會進入掛起狀態,只有對方響應之後才會重新恢復執行,所以為了避免在主執行緒進行耗時操作,需開啟子執行緒發起遠端操作。
3.aidl生成程式碼分析
我們來看下咱們上面的例子中根據aidl檔案自動生成的SchoolControllerAIDL類,來了解下它的實現原理:
public interface SchoolControllerAIDL extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.wangkeke.aidldemo.SchoolControllerAIDL { private static final java.lang.String DESCRIPTOR = "com.wangkeke.aidldemo.SchoolControllerAIDL"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.wangkeke.aidldemo.SchoolControllerAIDL interface, * generating a proxy if needed. */ public static com.wangkeke.aidldemo.SchoolControllerAIDL asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.wangkeke.aidldemo.SchoolControllerAIDL))) { return ((com.wangkeke.aidldemo.SchoolControllerAIDL) iin); } return new com.wangkeke.aidldemo.SchoolControllerAIDL.Stub.Proxy(obj); } @Override 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 { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getSchoolName: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.getSchoolName(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getSchools: { data.enforceInterface(DESCRIPTOR); java.util.List<com.wangkeke.aidldemo.School> _result = this.getSchools(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_registerListener: { data.enforceInterface(DESCRIPTOR); com.wangkeke.aidldemo.OnConnectSuccessLisener _arg0; _arg0 = com.wangkeke.aidldemo.OnConnectSuccessLisener.Stub.asInterface(data.readStrongBinder()); this.registerListener(_arg0); reply.writeNoException(); return true; } case TRANSACTION_unregisterListener: { data.enforceInterface(DESCRIPTOR); com.wangkeke.aidldemo.OnConnectSuccessLisener _arg0; _arg0 = com.wangkeke.aidldemo.OnConnectSuccessLisener.Stub.asInterface(data.readStrongBinder()); this.unregisterListener(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.wangkeke.aidldemo.SchoolControllerAIDL { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String getSchoolName(java.lang.String schoolNo) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(schoolNo); mRemote.transact(Stub.TRANSACTION_getSchoolName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.util.List<com.wangkeke.aidldemo.School> getSchools() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.wangkeke.aidldemo.School> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getSchools, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.wangkeke.aidldemo.School.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void registerListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null))); mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void unregisterListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null))); mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getSchoolName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getSchools = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); } public java.lang.String getSchoolName(java.lang.String schoolNo) throws android.os.RemoteException; public java.util.List<com.wangkeke.aidldemo.School> getSchools() throws android.os.RemoteException; public void registerListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException; public void unregisterListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException; }
可以看到SchoolControllerAIDL本身是個介面,繼承了IInterface介面,來看下IInterface的定義:
public interface IInterface { public IBinder asBinder(); }
我們來分別介紹一下SchoolControllerAIDL介面中幾個關鍵的方法和內部類,首先看到定義了一個DESCRIPTOR常量,它是Binder的唯一標識,asBinder()方法返回了當前的binder物件,asInterface方法的作用是用於將服務端的binder物件轉化成客戶端所需的AIDL介面型別的物件,並且可以看到,如果客戶端和服務端在一個程序,那麼直接返回服務端的Stub物件本身,否則返回封裝後的Stub.Proxy代理物件;onTransact方法的作用是根據客戶端的code來判斷客戶端請求的目標方法是什麼,然後根據Parcel中的序列化資料,data儲存了裝載資料,接著從data中取出目標方法所需的引數,然後執行目標方法,之後把函式返回值寫入到reply中。
注意,如果onTransact方法返回false,客戶端會請求失敗,我們在時機應用中肯定不希望隨便一個程序都能夠連線我們的遠端服務,因此可以用onTransact的返回值的特性來做許可權驗證。
接下來看一下內部類Proxy,它實現了SchoolControllerAIDL介面,它執行在客戶端,它也有一個asBinder方法,用來返回遠端binder物件,用來進行遠端呼叫,客戶端呼叫服務端的方法的時候,以getSchoolName為例,首先建立輸入型Parcel物件_data,輸出型Parcel物件_reply,以及返回值_result,然後把引數資訊寫入到_data中,就是通過它的transact方法來執行遠端操作的,transact內部呼叫了onTransact方法,注意,當呼叫transact的時候,當前執行緒會掛起,然後呼叫服務端的onTransact方法,直到onTransact執行結束返回後,當前執行緒繼續執行,從_reply中取出執行結果,賦值給result並返回。
圖解流程:

客戶端-伺服器流程
注意:當客戶端發起遠端請求時,由於當前執行緒會被掛起直到伺服器程序返回資料,如果遠端方法很耗時,那麼注意不能在Ui執行緒發起遠端請求;另外服務端的binder方法執行在binder執行緒池中,耗時操作也無需開啟新執行緒,因為其本身就是一個子執行緒了。
4.linkToDeath和unlinkToDeath
這倆方法是binder類比較重要的方法,因為binder執行在服務端程序中,如果服務端程序異常終止,那麼這時候binder連線就會斷開,導致我們的遠端請求失敗,而且對於客戶端來說,並不知道binder連線已經斷開,對於這個問題,binder提供了linkToDeath和unlinkToDeath方法來解決。
通過linkToDeath可以給binder設定一個死亡代理,當binder死亡時,就會收到通知,可以重新發起連線請求從而恢復,具體用法如下:
//定義死亡代理物件 private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if(null != schoolAIDL){ schoolAIDL.asBinder().unlinkToDeath(deathRecipient,0); schoolAIDL = null; //重新繫結service Intent intent = new Intent(ThirdProcessActivity.this,AidlService.class); bindService(intent, mConnection ,Context.BIND_AUTO_CREATE); } } };
在ServiceConnection的onServiceConnected方法中給binder設定死亡代理:
private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isConnection = true; schoolAIDL = SchoolControllerAIDL.Stub.asInterface(service); try { //設定死亡代理 service.linkToDeath(deathRecipient,0); schoolAIDL.registerListener(lisener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { isConnection = false; } };
此時當服務端死亡的時候,客戶端就會收到通知,然後就可以自己處理相應的邏輯了。
另外關於AIDL中定向tag的in,out,inout的理解和作用,大家可以看下這篇文章, 你真的理解AIDL中的in,out,inout麼?
6.Messenger的使用
Messenger同樣是android提供的一種IPC通訊方式,它是通過在程序間傳遞Message物件,通過Message的setData方法,傳遞Bundle物件,Bundle傳遞的資料必須實現Parcelable介面,Messener是序列工作的,不存在併發問題。
其實Messenger的底層實現就是AIDL,通過它的構造方法就會發現其中的端倪:
public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
它其實是對AIDL的封裝,讓我們不再需要編寫aidl檔案,就可以實現程序間通訊。下面來具體看下它的用法,首先看下服務端程式碼:
public class MessengerService extends Service { public static Messenger clientMessenger; public static final int MSG_FROM_CLIENT = 0; public static final int MSG_FROM_SERVICE = 1; // mServiceHandler private static Handler mServiceHandler = new Handler(){ public void handleMessage(Message msgFromClient) { switch (msgFromClient.what) { case MSG_FROM_CLIENT: //拿到客戶端發來的訊息 Log.e("wangkeke", "伺服器接收到的訊息:"+msgFromClient.getData().getString("client_msg")); //拿到客戶端的mClientMessenger Messenger mClientMessenger = msgFromClient.replyTo; clientMessenger = mClientMessenger; Message msgFromService = Message.obtain(null,MSG_FROM_SERVICE); Bundle bundle = new Bundle(); bundle.putString("service_msg", "這裡是伺服器,收到客戶端請求"); msgFromService.setData(bundle); try { //呼叫mClientMessenger.send將訊息傳送給客戶端 mClientMessenger.send(msgFromService); } catch (RemoteException e) { e.printStackTrace(); } break; } }; }; // mServiceMessenger關聯mServiceHandler private Messenger mServiceMessenger = new Messenger(mServiceHandler); @Override public IBinder onBind(Intent intent) { //將IBinder傳給客戶端,客戶端通過new Messenger(IBinder)拿到mServiceMessenger; return mServiceMessenger.getBinder(); } }
首先建立一個Messenger物件mServiceMessenger,它需要一個handler作為接收客戶端訊息的載體,在Service的onBind中返回服務端Messenger的getBinder()即可。
再來看下客戶端的程式碼:
//客戶端用來接收服務端訊息的Handler private static Handler mClientHandler = new Handler(){ public void handleMessage(Message msgFromService) { switch (msgFromService.what) { case MSG_FROM_SERVICE: Log.e("wangkeke", "客戶端:"+msgFromService.getData().getString("service_msg")); break; } }; }; //客戶端Messenger物件 private Messenger mClientMessenger = new Messenger(mClientHandler); conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder iBinder) { Log.e("wangkeke", "onServiceConnected handler 當前程序id:"+android.os.Process.myPid()+ "當前執行緒id:"+android.os.Process.myTid()); //拿到伺服器傳給客戶端的IBinder,建立服務端的Messenger mServiceMessenger = new Messenger(iBinder); //例項化一個Message Message msgFromClient = Message.obtain(null, MSG_FROM_CLIENT); Bundle bundle = new Bundle(); bundle.putString("client_msg", "已經和伺服器建立連線了"); msgFromClient.setData(bundle); //將mClientMessenger帶到伺服器去 msgFromClient.replyTo = mClientMessenger; try { //呼叫mServiceMessenger.send將訊息傳送的伺服器 mServiceMessenger.send(msgFromClient); } catch (RemoteException e) { e.printStackTrace(); } } }; //繫結Service Intent intent = new Intent(this,MessengerService.class); bindService(intent, conn, BIND_AUTO_CREATE); //客戶端給服務端主動發訊息 findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //新建message物件 Message msgFromClient = Message.obtain(null,MSG_FROM_CLIENT); //傳值 Bundle bundle = new Bundle(); bundle.putString("client_msg", "我是另一程序的客戶端發來的訊息"); msgFromClient.setData(bundle); //把客戶端的Messenger傳遞給服務端,使用雙向通訊 msgFromClient.replyTo = mClientMessenger; try { //通過服務端的Messenger傳送訊息 mServiceMessenger.send(msgFromClient); } catch (RemoteException e) { e.printStackTrace(); } } });
執行結果如下:

Messenger服務端和客戶端發訊息
可以看到使用Messenger傳送訊息確實很方便,而且無需我們再編寫aidl檔案,通過message訊息直接傳送即可,但是它也有明顯的缺點,要想服務端響應的話,需要在客戶端也建立一個Messenger,通過reply傳遞到服務端。
注意點:
1.Messenger一次只能處理一個請求(序列)/AIDL一次可以處理多個請求(並行);
2.Messenger不支援RPC,只能通過message傳遞訊息/AIDL支援RPC;
3.Messenger使用簡單,輕量級,不需要建立AIDL檔案/AIDL使用複雜,需要建立AIDL檔案;

AIDL和Messenger比較.png