Android AIDL使用介绍(3) 浅说AIDL背后的Binder
1.背景
在前面的博客中,我們已經(jīng)學(xué)會了使用AIDL進(jìn)行跨進(jìn)程通信,AIDL的使用比較簡單,可實際上跨進(jìn)程通信是一個相當(dāng)復(fù)雜的過程,例如進(jìn)程A是怎么找到進(jìn)程B的,如果有一個進(jìn)程C冒充進(jìn)程A,進(jìn)程B又該如何識別等等問題,而使用AIDL時,完全不用關(guān)心這復(fù)雜的過程,開發(fā)者只需關(guān)注業(yè)務(wù)邏輯即可,有句話說,哪有什么歲月靜好,只不過有人替我們負(fù)重前行,AIDL的背后肯定有機制幫我們完成了這些進(jìn)程間通訊的復(fù)雜操作,這個機制便是Binder。
Binder中文意思是黏合劑,這個名稱很貼切,生活中的黏合劑是將兩種材料通過界面的粘附和內(nèi)聚強度連接在一起的物質(zhì),而Android系統(tǒng)里,Binder則是將兩個進(jìn)程間黏合一起實現(xiàn)通訊。
今天就來了解一下Binder。
2.Binder發(fā)展歷史
Binder是Android系統(tǒng)里進(jìn)程間通信機制,源于OpenBinder,OpenBinder進(jìn)程間通信機制最早由Be公司提出,后來這個機制被Palm操作系統(tǒng)所采用,第一個版本也是在Palm的微內(nèi)核系統(tǒng)上實現(xiàn),后來Palm改用Linux操作系統(tǒng),OpenBinder也跟著移植到Linux并且開源,而Android系統(tǒng)基于Linux加上Google聘請了OpenBinder關(guān)鍵工程師Dianne Hackborn加入Android團隊,因此OpenBinder自然而然并入Android系統(tǒng),最初版本的Android時按原樣使用OpenBinder,隨后改名成Binder并完全重寫,成了今天Binder的樣子,值得一提的是,OpenBinder已經(jīng)不再維護(hù),算是壽終正寢,但其機制被Binder所繼承,從某種意義上來講,OpenBinder并未消亡,而是以Binder的形態(tài)繼續(xù)傳承下去。
3.為什么使用Binder
Android系統(tǒng)基于Linux,而Linux系統(tǒng)本身具有多種進(jìn)程間通信方式可供選擇,例如文件,Socket,Pipes,消息隊列等,為何陪嫁過來的Binder能夠后來居上,成為Android系統(tǒng)里進(jìn)程間通信的首選呢?
IPC要考慮的因素包括易用,安全,性能,Linux提供的原始IPC選項,易用性好的性能較差,例如Socket作為Client-Service通信方式,開發(fā)者使用較方便,但效率低,用于設(shè)備間通信較合適,用于進(jìn)程間通信則臃腫了;性能高的易用性和安全性較差,性能越高,往往越處于底層,底層的接口往往需要開發(fā)者考慮許多因素,例如在進(jìn)程終止時需及時釋放資源,否則容易發(fā)生內(nèi)存泄漏,而Binder則很好平衡了這三個因素:
Binder還有其他好處,包括原生支持多種數(shù)據(jù)類型,自定義類型使用過程也不復(fù)雜,支持同步和異步,自動回收資源等等。
Binder也因為優(yōu)秀的特性,已深深的和Android融合在一起,正如Hackborn自己所說的,在Android系統(tǒng)上,Binder幾乎用于所有跨進(jìn)程的操作。如果關(guān)掉Binder,整個系統(tǒng)將會是沒顯示,沒聲音,沒輸入,處于幾乎無法運作的狀態(tài),Binder對Android系統(tǒng)的重要性可見一斑。
3.Binder工作機制
一個客戶端即進(jìn)程A想和一個服務(wù)端即進(jìn)程B交互,由于進(jìn)程間的隔離,客戶端無法直接調(diào)用服務(wù)端,但內(nèi)核可以,因此他們使用Binder驅(qū)動捎話,傳遞消息。用日常生活的例子來說,進(jìn)程A是一個有多余儲蓄,想投資賺點收益投資者,進(jìn)程B是一個急著用錢的借款人,兩人并不熟悉,由于信息不對稱,投資者不會直接借錢給借款人,因此也需要一個金融中介,對應(yīng)于Binder驅(qū)動。
由于Binder驅(qū)動在內(nèi)核層,內(nèi)核層的接口協(xié)議比較繁瑣,客戶端和服務(wù)端并不想了解底層協(xié)議,只想專注于業(yè)務(wù)開發(fā),于是他們使用代理(客戶端是Proxy,服務(wù)端是Stub)來和Binder驅(qū)動打交道,還是用金融中介概念理解,投資人當(dāng)然可以直接和金融中介例如銀行打交道,但越底層的業(yè)務(wù)辦起來越羅嗦,例如銀行上班時間和我們一樣,我們下班了他們也下班,想辦點業(yè)務(wù)還得請假,去到營業(yè)廳還得排隊,還需要填各種表格,這時候可以找代理幫忙跑腿,省了很多事。
客戶端的代理Proxy和服務(wù)端的代理Stub不用開發(fā)者自己實現(xiàn),而是依靠AIDL工具,回憶一下AIDL的使用過程,當(dāng)我們新建一個AIDL文件時,Android SDK工具根據(jù)AIDL的描述,自動生成一個同名的java文件,java文件中就包含了Proxy和Stub,以及與Binder驅(qū)動通信的代碼。這也是為什么反復(fù)強調(diào),當(dāng)更新AIDL文件時,一定要及時同步工程,因為Android SDK工具可能不及時更新同名java文件。
最后一個問題是,Android系統(tǒng)提供的服務(wù)端多種多樣,客戶端怎么聯(lián)系指定的服務(wù)端,這需要依靠Context Manager,Context Manager提供服務(wù)的登記和查詢功能,類似于現(xiàn)實生活中的銀保監(jiān)會,金融機構(gòu)需要在銀保監(jiān)會備案,在Android系統(tǒng)中,使用Service Manager實現(xiàn)Context Manager功能,Service Manager是所有服務(wù)中第一個啟動的,其啟動后,其他服務(wù)才能登記注冊。
服務(wù)的向Context Manager注冊和客戶端向Context Manager查詢服務(wù)的過程,其實也是一個跨進(jìn)程通信過程,那么服務(wù)端又怎么知道Context Manager的句柄?難道又要弄一個管理Context Manager的死循環(huán)過程嗎,這倒是沒必要,給予Context Manager一個固定的地址即可。過程見下圖。
4.AIDL源碼分析
仍以上篇博客創(chuàng)建的AIDL工程為例子(見Android AIDL使用介紹(2)),在該工程中,創(chuàng)建了AIDL例子,Android Studio自動在對應(yīng)的
build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\包名
目錄下生成對應(yīng)的同名java文件,路徑見圖
打開看一下里面的內(nèi)容,代碼一開始就有注釋提醒我們,這是自動生成的代碼,請勿修改,因為是自動生成的代碼,可讀性上稍微差一點。
/** This file is auto-generated. DO NOT MODIFY.*/ package com.pm.service; public interface ServiceAidlInterface extends android.os.IInterface {/** Default implementation for ServiceAidlInterface. */public static class Default implements com.pm.service.ServiceAidlInterface{@Override public java.lang.String ServiceGreet() throws android.os.RemoteException{return null;}@Override public java.util.List<com.pm.service.Student> getStudentList() throws android.os.RemoteException{return null;}@Overridepublic android.os.IBinder asBinder() {return null;}}/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.pm.service.ServiceAidlInterface{private static final java.lang.String DESCRIPTOR = "com.pm.service.ServiceAidlInterface";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.pm.service.ServiceAidlInterface interface,* generating a proxy if needed.*/public static com.pm.service.ServiceAidlInterface asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.pm.service.ServiceAidlInterface))) {return ((com.pm.service.ServiceAidlInterface)iin);}return new com.pm.service.ServiceAidlInterface.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{java.lang.String descriptor = DESCRIPTOR;switch (code){case INTERFACE_TRANSACTION:{reply.writeString(descriptor);return true;}case TRANSACTION_ServiceGreet:{data.enforceInterface(descriptor);java.lang.String _result = this.ServiceGreet();reply.writeNoException();reply.writeString(_result);return true;}case TRANSACTION_getStudentList:{data.enforceInterface(descriptor);java.util.List<com.pm.service.Student> _result = this.getStudentList();reply.writeNoException();reply.writeTypedList(_result);return true;}default:{return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.pm.service.ServiceAidlInterface{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 ServiceGreet() 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);boolean _status = mRemote.transact(Stub.TRANSACTION_ServiceGreet, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().ServiceGreet();}_reply.readException();_result = _reply.readString();}finally {_reply.recycle();_data.recycle();}return _result;}@Override public java.util.List<com.pm.service.Student> getStudentList() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.pm.service.Student> _result;try {_data.writeInterfaceToken(DESCRIPTOR);boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getStudentList();}_reply.readException();_result = _reply.createTypedArrayList(com.pm.service.Student.CREATOR);}finally {_reply.recycle();_data.recycle();}return _result;}public static com.pm.service.ServiceAidlInterface sDefaultImpl;}static final int TRANSACTION_ServiceGreet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);public static boolean setDefaultImpl(com.pm.service.ServiceAidlInterface impl) {if (Stub.Proxy.sDefaultImpl == null && impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.pm.service.ServiceAidlInterface getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}public java.lang.String ServiceGreet() throws android.os.RemoteException;public java.util.List<com.pm.service.Student> getStudentList() throws android.os.RemoteException; }代碼的層次分布見下圖
- 首先是定義抽象方法的聲明,這些函數(shù)就是我們在aidl文件定義的抽象方法,包括ServiceGreet()和getStudentList();
- 其次是抽象方法的缺省實現(xiàn),在AIDL工程中,我們已經(jīng)在Service里實現(xiàn)了抽象方法,假如沒有實現(xiàn)的話,則使用自動生成的缺省實現(xiàn);
- 再次是內(nèi)部類Stub,Stub是Binder的子類,跨進(jìn)程調(diào)用由這一個內(nèi)部類完成,當(dāng)客戶端和服務(wù)端不在同一個進(jìn)程時,走transaction(交易)過程,在當(dāng)前語境下,transaction指的是一個進(jìn)程傳遞數(shù)據(jù)給另一個進(jìn)程,傳遞的數(shù)據(jù)按照一定的格式要求組包,transaction的業(yè)務(wù)邏輯由Stub的內(nèi)部代理類Proxy來完成;
- 接下來便是onTransact(int code, Parcel data, Parcel reply, int flag),這是處理transaction的具體邏輯,這也是客戶端和服務(wù)端直接溝通的渠道,當(dāng)客戶端發(fā)起請求時,這個方法來處理請求,服務(wù)端通過code獲取客戶端想要訪問的目標(biāo)方法,這個code是方法的編號,代碼中已經(jīng)聲明static final int TRANSACTION_ServiceGreet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 第二個參數(shù)data用于獲取目標(biāo)方法所需的參數(shù),注意是Parcel類型,當(dāng)服務(wù)端執(zhí)行完目標(biāo)方法后,將返回值寫入到reply中;
- 其他重要接口如asInterface()用于判斷當(dāng)前進(jìn)程是服務(wù)端進(jìn)程還是客戶端進(jìn)程,如果是服務(wù)端進(jìn)程則返回Stub對象,否則返回Stub.Proxy對象;DESCRIPTOR用于表示Binder的唯一標(biāo)識,使用當(dāng)前當(dāng)前Binder的包路徑表示;asBinder()返回當(dāng)前Binder對象。
4.總結(jié)
本文主要分析AIDL背后使用到的進(jìn)程間通信機制Binder,Binder源自于OpenBinder,后來隨作者陪嫁到Android系統(tǒng),憑借著易用性好,性能高,安全性能好,后來居上,成為Android進(jìn)程間通信機制,成為Android不可或缺的一部分。
Binder的機制細(xì)節(jié)其實還有很多,本文只是嘗試簡化說明,如有錯漏,歡迎指正。
參考目錄:
1.Deep Dive into Android IPC/Binder Framework at Android Builders Summit 2013;
2.Android’s Binder – in depth
3.https://www.cnblogs.com/zc9527/p/5638688.html;
4.https://www.cnblogs.com/itgungnir/p/6640120.html
總結(jié)
以上是生活随笔為你收集整理的Android AIDL使用介绍(3) 浅说AIDL背后的Binder的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android AIDL使用介绍(2)自
- 下一篇: Android持久化存储(1)文件存储