Android進階——Android跨進程通訊機制之Binder、IBinder、Parcel、AIDL
同步滾動:開
前言
Binder機制是Android系統提供的跨進程通訊機制,這篇文章開始會從Linux相關的基礎概念知識開始介紹,從基礎概念知識中引出Binder機制,歸納Binder機制與Linux系統的跨進程機制的優缺點,接著分析Binder的通信模型和原理,而Binder機制最佳體現就是AIDL,所以在後面會分析AIDL的實現原理,最後簡單的提下AMS的Binder體系,整篇文章中間會穿插有IBinder、Binder、Parcel的介紹,整篇文章閱讀難度不大,不會涉及到framework層的Binder原理,AIDL部分需要有AIDL的使用基礎
基礎概念
基礎概念部分介紹Linux的某些機制,主要想表達Binder驅動的出現的原因,如果對Linux熟悉的可以直接跳過這部分,看第五點即可
一、進程隔離
出於安全考慮,一個進程不能操作另一個進程的數據,進而一個操作系統必須具備進程隔離這個特性。在Linux系統中,虛擬內存機制為每個進程分配了線性連續的內存空間,操作系統將這種虛擬內存空間映射到物理內存空間,每個進程有自己的虛擬內存空間,進而不能操作其他進程的內存空間,每個進程只能操作自己的虛擬內存空間,只有操作系統才有權限操作物理內存空間。
進程隔離保證了每個進程的內存安全,但是在大多數情形下,不同進程間的數據通訊是不可避免的,因此操作系統必須提供跨進程通信機制
二、用戶空間和內核空間
用戶空間:表示進程運行在一個特定的操作模式中,沒有接觸物理內存或設備的權限內核空間:表示獨立於普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限
三、系統調用/內核態/用戶態
抽象來看,操作系統中安全邊界的概念就像環路的概念一樣(前提是系統支持這種特性),一個環上持有一個特定的權限組,Intel 支持四層環,但是 Linux 只使用了其中的兩環(0號環持有全部權限,3號環持有最少權限,1號和2號環未使用),系統進程運行在1號環,用戶進程運行在3號環,如果一個用戶進程需要其他高級權限,其必須從3號環過渡成0號環,過渡需要通過一個安全參數檢查的網關,這種過渡被稱為系統調用,在執行過程中會產生一定數量的計算開銷。所以,用戶空間要訪問內核空間的唯一方式就是系統調用(System Call)
四、內核模塊/驅動
通過系統調用,用戶空間可以訪問內核空間,它是怎麼做到訪問內核空間的呢?Linux的動態可加載內核模塊機制解決了這個問題,模塊是具有獨立功能的程序,它可以被單獨編譯,但不能獨立運行。這樣,Android系統可以通過添加一個內核模塊運行在內核空間,用戶進程之間的通過這個模塊作為橋樑,就可以完成通信了。在Android系統中,這個運行在內核空間的,負責各個用戶進程通過Binder通信的內核模塊叫做Binder驅動
五、簡單的總結
將前面的所有概念連接起來理解就會非常好消化知識點:
1.Linux的虛擬內存機制導致內存的隔離,進而導致進程隔離2.進程隔離的出現導致對內存的操作被劃分為用戶空間和內核空間3.用戶空間需要跨權限去訪問內核空間,必須使用系統調用去實現4.系統調用需要藉助內核模塊/驅動去完成
前三步決定了進程間通訊需要藉助內核模塊/驅動去實現,而Binder驅動就是內核模塊/驅動中用來實現進程間通訊的
為什麼要用Binder
Linux提供有管道、消息隊列、信號量、內存共享、套接字等跨進程方式,那為什麼Android要選擇Binder另起爐灶呢?
一、傳輸性能好
- Socket:是一個通用接口,導致其傳輸效率低,開銷大
- 共享內存:雖然在傳輸時不需要拷貝數據,但其控制機制複雜
- Binder:複雜數據類型傳遞可以複用內存,需要拷貝1次數據
- 管道和消息隊列:採用存儲轉發方式,至少需要拷貝2次數據,效率低
二、安全性高
- 傳統的進程:通信方式對於通信雙方的身份並沒有做出嚴格的驗證,只有在上層協議上進行架設
- Binder機制:從協議本身就支持對通信雙方做身份校檢,因而大大提升了安全* 性
Binder通信模型
首先在理解模型之前先熟悉這幾個概念:
- Client進程:跨進程通訊的客戶端(運行在某個進程)
- Server進程:跨進程通訊的服務端(運行在某個進程)
Binder驅動:跨進程通訊的介質 - ServiceManager:跨進程通訊中提供服務的註冊和查詢(運行在System進程)
這裡只是個簡單的模型而已,只需理解模型的通訊流程:
1.Server端通過Binder驅動在ServiceManager中註冊2.Client端通過Binder驅動獲取ServiceManager中註冊的Server端3.Client端通過Binder驅動和Server端進行通訊
Binder通信原理
理解完模型流程之後,開始理解模型的通訊原理:
1.Service端通過Binder驅動在ServiceManager的查找表中註冊Object對象的add方法2.Client端通過Binder驅動在ServiceManager的查找表中找到Object對象的add方法,並返回proxy對象的add方法,add方法是個空實現,proxy對象也不是真正的Object對象,是通過Binder驅動封裝好的代理類的add方法3.當Client端調用add方法時,Client端會調用proxy對象的add方法,通過Binder驅動去請求ServiceManager來找到Service端真正對象,然後調用Service端的add方法
Binder對象和Binder驅動
- Binder對象:Binder機制中進行進程間通訊的對象,對於Service端為Binder本地對象,對於Client端為Binder代理對象
- Binder驅動:Binder機制中進行進程間通訊的介質,Binder驅動會對具有跨進程傳遞能力的對象做特殊處理,自動完成代理對象和本地對象的轉換
由於Binder驅動會對具有跨進程傳遞能力的對象做特殊處理,自動完成代理對象和本地對象的轉換,因此在驅動中保存了每一個跨越進程的Binder對象的相關信息,Binder本地對象(或Binder實體)保存在binder_node的數據結構,Binder代理對象(或Binder引用/句柄)保存在binder_ref的數據結構
Java層的Binder
- Binder類:是Binder本地對象
- BinderProxy類:是Binder類的內部類,它代表遠程進程的Binder對象的本地代理
- Parcel類:是一個容器,它主要用於存儲序列化數據,然後可以通過Binder在進程間傳遞這些數據
- IBinder接口:代表一種跨進程傳輸的能力,實現這個接口,就能將這個對象進行跨進程傳遞
- IInterface接口:client端與server端的調用契約,實現這個接口,就代表遠程server對象具有什麼能力,因為IInterface接口的asBinder方法的實現可以將Binder本地對象或代理對象進行返回
Binder類和BinderProxy類都繼承自IBinder,因而都具有跨進程傳輸的能力,在跨越進程的時候,Binder驅動會自動完成這兩個對象的轉換。IBinder是遠程對象的基本接口,是為高性能而設計的輕量級遠程調用機制的核心部分,但它不僅用於遠程調用,也用於進程內調用。IBinder接口定義了與遠程對象交互的協議,建議不要直接實現這個接口,而應該從Binder派生。Binder實現了IBinder接口,但是一般不需要直接實現此類,而是跟據你的需要由開發包中的工具生成,這個工具叫aidi。你通過aidi語言定義遠程對象的方法,然後用aidi工具生成Binder的派生類,然後使用它
AIDL
由於編譯工具會給我們生成一個Stub的靜態內部類,這個類繼承了Binder, 說明它是一個Binder本地對象,它實現了IInterface接口,表明它具有遠程Server承諾給Client的能力
一、服務端
在服務端中,我們只要實現Stub抽象類,和實現其方法即可
<code>private
IBinder myS =new
IMyAidlInterface.Stub() {public
void
basicTypes
(
int
anInt,long
aLong,boolean
aBoolean,float
aFloat,double
aDouble, String aString)throws
RemoteException { }public
int
add
(
int
num1,int
num2)throws
RemoteException { Log.i("Hensen"
,"從客戶端發來的AIDL請求:num1->"
+ num1 +"::num2->"
+ num2);return
num1 + num2; } }; /<code>
二、客戶端
在客戶端中,可以通過bindService的回調中獲取AIDL接口
<code>private
ServiceConnection conn =new
ServiceConnection() {public
void
onServiceConnected
(ComponentName name, IBinder service)
{ iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service); }public
void
onServiceDisconnected
(ComponentName name)
{ iMyAidlInterface =null
; } };public
void
add
(View view)
{try
{int
res = iMyAidlInterface.add(1
,2
); Log.i("Hensen"
,"從服務端調用成功的結果:"
+ res); }catch
(RemoteException e) { e.printStackTrace(); } } /<code>
梳理客戶端的調用流程:
1.調用Stub.asInterface獲取BinderProxy對象2.調用BinderProxy對象的add方法
三、分析原理
1、Stub
Stub類繼承自Binder,意味著這個Stub其實自己是一個Binder本地對象,然後實現了IMyAidlInterface接口,IMyAidlInterface本身是一個IInterface,因此他攜帶某種客戶端需要的能力(這裡是方法add)。此類有一個內部類Proxy,也就是Binder代理對象
<code>package
com.handsome.boke;public
interface
IMyAidlInterface
extends
android
.os
.IInterface
{public
static
abstract
class
Stub
extends
android
.os
.Binder
implements
com
.handsome
.boke
.IMyAidlInterface
{private
static
final
java.lang.String DESCRIPTOR ="com.handsome.boke.IMyAidlInterface"
;public
Stub
()
{this
.attachInterface(this
, DESCRIPTOR); }public
static
com.handsome.boke.IMyAidlInterface
asInterface
(android.os.IBinder obj)
{if
((obj ==null
)) {return
null
; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if
(((iin !=null
) && (iininstanceof
com.handsome.boke.IMyAidlInterface))) {return
((com.handsome.boke.IMyAidlInterface) iin); }return
new
com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj); }public
android.os.IBinder
asBinder
()
{return
this
; }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_basicTypes: { data.enforceInterface(DESCRIPTOR);int
_arg0; _arg0 = data.readInt();long
_arg1; _arg1 = data.readLong();boolean
_arg2; _arg2 = (0
!= data.readInt());float
_arg3; _arg3 = data.readFloat();double
_arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString();this
.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException();return
true
; }case
TRANSACTION_add: { data.enforceInterface(DESCRIPTOR);int
_arg0; _arg0 = data.readInt();int
_arg1; _arg1 = data.readInt();int
_result =this
.add(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result);return
true
; } }return
super
.onTransact(code, data, reply, flags); }private
static
class
Proxy
implements
com
.handsome
.boke
.IMyAidlInterface
{private
android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; }public
android.os.IBinder
asBinder
()
{return
mRemote; }public
java.lang.String
getInterfaceDescriptor
()
{return
DESCRIPTOR; }public
void
basicTypes
(
int
anInt,long
aLong,boolean
aBoolean,float
aFloat,double
aDouble, java.lang.String aString)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.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1
) : (0
))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply,0
); _reply.readException(); }finally
{ _reply.recycle(); _data.recycle(); } }public
int
add
(
int
num1,int
num2)throws
android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain();int
_result;try
{ _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(num1); _data.writeInt(num2); mRemote.transact(Stub.TRANSACTION_add, _data, _reply,0
); _reply.readException(); _result = _reply.readInt(); }finally
{ _reply.recycle(); _data.recycle(); }return
_result; } }static
final
int
TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION +0
);static
final
int
TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION +1
); }public
void
basicTypes
(
int
anInt,long
aLong,boolean
aBoolean,float
aFloat,double
aDouble, java.lang.String aString)throws
android.os.RemoteException;public
int
add
(
int
num1,int
num2)throws
android.os.RemoteException; } /<code>
2、asInterface
當客戶端bindService的onServiceConnecttion的回調裡面,通過asInterface方法獲取遠程的service的。其函數的參數IBinder類型的obj,這個對象是驅動給我們的,如果是Binder本地對象,那麼它就是Binder類型,如果是Binder代理對象,那就是BinderProxy類型。asInterface方法中會調用queryLocalInterface,查找Binder本地對象,如果找到,說明Client和Server都在同一個進程,這個參數直接就是本地對象,直接強制類型轉換然後返回,如果找不到,說明是遠程對象那麼就需要創建Binder代理對象,讓這個Binder代理對象實現對於遠程對象的訪問
<code>public
static
com.handsome.boke.IMyAidlInterface
asInterface
(android.os.IBinder obj
) {if
((obj ==null
)) {return
null
; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if
(((iin !=null
) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {return
((com.handsome.boke.IMyAidlInterface) iin); }return
new
com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj); } /<code>
3、add
當客戶端調用add方法時,首先用Parcel把數據序列化,然後調用mRemote.transact方法,mRemote就是new Stub.Proxy(obj)傳進來的,即BinderProxy對象
<code>public
int
add
(
int
num1,int
num2)throws
android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain();int
_result;try
{ _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(num1); _data.writeInt(num2); mRemote.transact(Stub.TRANSACTION_add, _data, _reply,0
); _reply.readException(); _result = _reply.readInt(); }finally
{ _reply.recycle(); _data.recycle(); }return
_result; } /<code>
4、transact
BinderProxy的transact方法是native方法,它的實現在native層,它會去借助Binder驅動完成數據的傳輸,當完成數據傳輸後,會喚醒Server端,調用了Server端本地對象的onTransact函數
<code>public
native
boolean
transact
(
int
code, Parcel data, Parcel reply,int
flags)throws
RemoteException; /<code>
5、onTransact在Server進程裡面,onTransact根據調用code會調用相關函數,接著將結果寫入reply並通過super.onTransact返回給驅動,驅動喚醒掛起的Client進程裡面的線程並將結果返回
<code>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_add: { data.enforceInterface(DESCRIPTOR);int
_arg0; _arg0 = data.readInt();int
_arg1; _arg1 = data.readInt();int
_result =this
.add(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result);return
true
; } }return
super
.onTransact(code, data, reply, flags); } /<code>
6、題外話
為什麼生成的文件不直接分為1個接口,2個類,清晰明瞭。Android這樣子設計是有道理的,當有多個AIDL文件時候,Stub和Proxy類就會重名,把它們放在各自的AIDL接口中,就區分開來了
AMS的Binder體系
AMS是Android中最核心的服務,主要負責系統中四大組件的啟動、切換、調度及應用進程的管理和調度等工作,從圖中可以看出:
結語
這裡只是簡單的理解下Binder機制的基本原理,後續有時間會研究framework層的知識,如果有興趣的同學可以不依賴AIDL工具,手寫遠程Service完成跨進程通信,這樣就可以加深對AIDL和Binder的理解,個人覺得這樣是最好的記憶方式,一起來寫吧
文章不易,如果大家喜歡這篇文章,或者對你有幫助希望大家多多,點贊,轉發,關注 哦。文章會持續更新的。絕對乾貨!!!