Android進階-跨進程通訊機制之Binder、IBinder、Parcel、AIDL

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)

Android進階-跨進程通訊機制之Binder、IBinder、Parcel、AIDL

四、內核模塊/驅動

通過系統調用,用戶空間可以訪問內核空間,它是怎麼做到訪問內核空間的呢?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進程)
Android進階-跨進程通訊機制之Binder、IBinder、Parcel、AIDL

這裡只是個簡單的模型而已,只需理解模型的通訊流程:

1.Server端通過Binder驅動在ServiceManager中註冊2.Client端通過Binder驅動獲取ServiceManager中註冊的Server端3.Client端通過Binder驅動和Server端進行通訊

Binder通信原理

Android進階-跨進程通訊機制之Binder、IBinder、Parcel、AIDL

理解完模型流程之後,開始理解模型的通訊原理:

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

) && (iin

instanceof

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體系

Android進階-跨進程通訊機制之Binder、IBinder、Parcel、AIDL

AMS是Android中最核心的服務,主要負責系統中四大組件的啟動、切換、調度及應用進程的管理和調度等工作,從圖中可以看出:

Android進階-跨進程通訊機制之Binder、IBinder、Parcel、AIDL

結語

這裡只是簡單的理解下Binder機制的基本原理,後續有時間會研究framework層的知識,如果有興趣的同學可以不依賴AIDL工具,手寫遠程Service完成跨進程通信,這樣就可以加深對AIDL和Binder的理解,個人覺得這樣是最好的記憶方式,一起來寫吧

文章不易,如果大家喜歡這篇文章,或者對你有幫助希望大家多多,點贊,轉發,關注 哦。文章會持續更新的。絕對乾貨!!!


分享到:


相關文章: