AIDL:Android Interface Definition Language,即Android接口定義語言,是Android進程間通信比較常用的一種方式。翻譯一下,就是為了讓某個Service與多個應用程序組件之間進行跨進程通信,從而實現多個應用程序共享同一個Service,以此來實現不同APP間的交互。

 

可以對上面這段話進行一些拆解。

 

什么是接口定義語言

IDL,接口定義語言(Interface Definition Language),也叫接口描述語言(Interface Description Language),是一個描述軟件組件接口的語言規范。通過IDL文件定義公共的應用程序接口(API),再由服務程序中的對象向外公布。

From wiki:

An interface description language or interface definition language (IDL), is a specification language used to describe a software component's application programming interface (API).

 

什么是進程間通信

進程間通信(IPC,Interprocess communication)是一組編程接口,讓程序員能夠協調不同的進程,使之能在一個操作系統里同時運行,并相互傳遞、交換信息。簡單來說,進程間通信就是指進程間數據交互的過程,是一個進程與另一個進程間共享消息的一種通信方式。

消息(message)是發送進程形成的一個消息塊,將消息內容傳送給接收進程。IPC機制是消息從一個進程的地址空間拷貝到另一個進程的地址空間。

 

補充說明:

按照操作系統中的描述,進程一般指一個執行單元,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎,在PC和移動設備上指一個程序或者一個應用;一個應用至少有一個進程(當然也可以擁有多個進程),一個進程中可以包含了多個線程,但至少要包含一個線程(線程是CPU執行的最小調度單元,也是一種有限的系統資源)。進程相當于是線程的容器,線程可以與進程內的其他線程一起共享進程的資源。

在android中,主線程也叫UI線程,在UI線程里才能操作界面元素。很多時候,一個進程中需要執行大量的、耗時的任務。如果這些任務放在主線程中去執行就會造成界面無法響應,也就是我們常說的“ANR”(Application Not Responding)。

 

進程間通信的原因

在Linux系統中,虛擬內存機制為每個進程分配了線性連續的內存空間,操作系統將這種虛擬內存空間映射到物理內存空間,每個進程有自己的虛擬內存空間。因此每個進程只能操作自己的虛擬內存空間,而不能操作其他進程的內存空間,只有操作系統才有權限操作物理內存空間,這就是進程隔離。進程隔離保證了每個進程的內存安全,但是在大多數情形下,不同進程間的數據通訊是不可避免的,因此操作系統必須提供跨進程通信機制。

 

進程間通信的目的

數據傳輸:一個進程需要將它的數據發送給另一進程,發送的數據量在一個字節到幾兆字節之間。

共享數據:多個進程想要操作共享數據,一個進程對共享數據的修改,別的進程應該立刻看到。

事件通知:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。

資源共享:多個進程之間共享同樣的資源。為了做到這一點,需要內核提供鎖和同步機制。

進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,并能夠及時知道它的狀態改變

 

進程間通信的原理

 

每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數據必須通過內核。在內核中開辟一塊緩沖區,進程1把數據從用戶空間拷到內核緩沖區,進程2再從內核緩沖區把數據讀走,內核提供的這種機制稱為進程間通信機制(IPC機制)。

主要的過程如下圖所示:

 

IPC機制是現代操作系統中都存在的機制,而對于Android來說,它雖然是基于Linux內核的移動操作系統,但它的進程間通信并不能完全繼承于Linux。因此,為了使其他的應用程序也能夠訪問本應用程序提供的服務,Android系統采用了遠程過程調用(RPC)方式來實現。與很多其他的基于RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。

補充:IDL通常用于RPC軟件,它提供了一個“橋梁”用來連接不同的系統。

 

什么是RPC

RPC(Remote Procedure Call)即遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,在不需要了解底層網絡技術的協議下,即可獲取計算機進程中的數據。RPC使得開發包括網絡分布式多程序在內的應用程序更加容易。

 

RPC在OSI網絡通信7層模型中位于會話層:

 

 

RPC與IPC的關系

Android 利用遠程過程調用 (RPC) 提供了一種進程間通信 (IPC) 機制,通過這種機制,由 Activity 或其他應用組件調用的方法可(在其他進程中)遠程執行,而所有結果將返回給調用方。

這就要求把方法調用及其數據分解至操作系統可以識別的程度,并將其從本地進程和地址空間傳輸至遠程進程和地址空間,然后在遠程進程中重新組裝并執行該調用。 然后,返回值將沿相反方向傳輸回來。 Android 提供了執行這些 IPC 事務所需的全部代碼,因此只需進行定義和實現 RPC 編程接口即可。

也就是說,RPC在Android的具體體現是依賴 bindService() 的方式,通過 onBind 方法將服務端的計算結果返回給客戶端(Activity等組件)的過程。

備注:要執行 IPC,必須使用 bindService() 將應用綁定到服務上。

 

 

既然說到了Android應用程序的組件,那這里也簡單提一句:Android系統中4種應用程序組件分別為——Activity、Content Provider、Broadcast Receiver和Service。

  

Android SDK中提供的四種跨進程通信的方式正好對應于上面的四種組件:

① Activity可以跨進程調用其他應用程序的Activity;

② Content Provider可以跨進程訪問其他應用程序中的數據(以Cursor對象形式返回),也可以對其他應用程序的數據進行增、刪、改、查操作;

③ Broadcast可以向android系統中所有應用程序發送廣播,需要跨進程通訊的應用程序可以監聽這些廣播;

④ Service和Content Provider類似,也可以訪問其他應用程序中的數據,但不同的是:ContentProvider返回的是Cursor對象,而Service返回的是Java對象,這種可以跨進程通訊的服務叫AIDL服務。

 

可以用一張圖來表示:

 

補充:

① 廣播有一個缺點,在手機中廣播較多時會有明顯的延時,甚至有廣播發送不成功的情況出現,因此利用AIDL Servic實現跨進程通信應運而生,雖然實現上稍微麻煩點兒。

② 注意普通的Service并不能實現跨進程操作,實際上普通的Service和它所在的應用處于同一個進程中,而它也不會專門開一條新的線程,因此如果在普通的Service中實現耗時的任務,需要新開線程;因此,要實現跨進程通信,需要借助AIDL。Android中的跨進程服務其實是采用C/S的架構,因而AIDL的目的就是實現通信接口。

③ 也就是說,AIDL其實就是Android中眾多進程間通信方式中的其中一種方式。

 

那么,具體實現進程間通信的方式有什么呢?下面就介紹下幾種常用方式:

 

1. Bundle(組件間)

我們知道,四大組件中的三大組件(Activity,Service,Receiver)都是支持在Intent中傳遞Bundle數據的,由于Bundle實現Parcelable接口,所以它可以方便地在不同的進程中傳輸。基于這一點,當我們在一個進程中啟動了另一個進程的Activity、Service、Receiver,就可以在Bundle中附加我們需要傳輸給遠程進程的信息并通過Intent發送出去。當然,我們傳輸的數據必須被序列化,比如基本數據類型、實現了Parcelable的對象,實現了Serializable的對象以及一些android支持的特殊對象,具體內容可以看Bundle這個類,就可以看到所有它支持的類型。Bundle不支持的類型我們無法通過進程間傳遞數據。

 

2. 文件共享

共享文件也是一種不錯的進程間的通信方式。兩個進程通過讀/寫同一個文件來交換數據。比如A進程把數據寫入文件,B進程通過讀取這個文件來獲取數據。我們在windows上,一個文件如果被加了排斥鎖將會導致其他線程無法對其進行訪問,包括讀和寫。不過因為android系統基于Linux,使其并發讀/寫可以沒有任何限制的進行,甚至可以兩個線程同時對一個文件進行寫操作(雖然這可能會出問題)。通過文件交換數據很好使用,除了可以交換一些文本信息外,我們還可以在序列化一個對象到文件系統的同時,從另一個進程中恢復這個對象。反序列化得到的對象只是在內容上和序列化的對象是一樣的,但它們本質上是兩個對象。

當然,SharePreferences是個特例。SharePreferences是Android中提供的輕量級的存儲方案,它通過鍵值對的方式來存儲數據,在底層實現上它采用xml文件來存儲鍵值對,每個應用的SharePreferences文件都可以在當前所在的data目錄下查看到。從本質上來說,SharePreferences也屬于文件的緩存,因此在多進程模式下,系統對它的讀/寫就變的不可靠,當面對高并發的模式讀/寫訪問,SharePrefenerces有很大幾率會丟失數據,因此,不建議在進程通信中使用SharePreferences。

 

3. Messenger(基于Binder)

Messager翻譯為信使,顧名思義,通過它可以在不同進程間傳遞Message對象,在Message中放入我們需要傳遞的數據,就可以輕松地實現數據進程間傳遞了。Messenger是一種輕量級的IPC,它的底層實現是AIDL,從其兩個構造方法中就可以明顯看出AIDL的痕跡,不管是I Messenger還是Stub.asInterface,這種使用方法都表明它的底層是AIDL。

public Messager(Handler target){
    mTarget = target.getIMessenger();
}

public Messenger(IBinder target){
    mTarget = IMessenger.Stub.asInterface(target);
}

Messenger的使用方法很簡單,它對AIDL做了封裝,使得我們可以更簡單地進行進程間通信。同時,由于它一次處理一個請求,因此在服務端我們不用考慮線程同步的問題,這是因為在服務端中不存在并發執行的情形。實現一個Messager有如下步驟,分為客戶端和服務端:

服務端進程

首先,我們需要在服務端創建一個Service來處理客戶端的鏈接請求,同時創建一個Handler ,并通過它來創建一個Messenger對象,然后在Service的onBind中返回這個Messenger對象底層的Binder即可。

客戶端進程

客戶端進程中,首先要綁定服務端的Service,綁定成功后用服務端返回的IBinder對象創建一個Messenger,通過這個Messenger就可以向服務端發送消息了,發消息類型為Message對象。如果需要服務端能夠回應客戶端,就和服務端一樣,我們還需要創建一個Handler并創建一個新的Messenger,并把這個Messenger對象通過Message的replyTo參數傳遞給服務端,服務端通過這個replyTo參數就可以回應客戶端

 

4. AIDL(基于Binder)

Messager是以串行的方式來進行處理客戶端發來的消息,如果大量的消息同時發送到服務器,服務端仍然只能一個個處理,如果有大量的并發請求,那么用Messenger顯然就不太合適了。同時,Messenger的作用主要是為了傳遞消息,很多時候我們可能需要跨進程調用服務端的方法,這種情形下Messager就無法做到了,但是我們可以使用AIDL來實現跨進程的方法調用。AIDL也是Messenger的底層實現,因此Messenger本質上也是AIDL,只不過系統為我們做了封裝,從而方便上層的調用而已。

服務端

服務端首先要創建一個Service用來監聽客戶端的鏈接請求,然后創建一個AIDL文件,將暴露給客戶的接口在這個AIDL文件中聲明,最后在Service中實現這個AIDL接口即可。

客戶端

客戶端所做的事情就要稍微簡單一些,首先需要綁定服務端的Service,綁定成功后,將服務端返回的Binder對象轉成AIDL接口所屬的類型,接著就可以調用AIDL中的方法了。

AIDL接口的創建

首先先看AIDL接口的創建,我們創建了一個后綴名為AIDL的文件,在里面聲明了一個接口和兩個接口的方法

 

在AIDL中并不是所有的數據都能使用的,AIDL支持以下數據類型的數據傳輸:

Java的8種基本數據類型:int、long、char、boolean、double等

String和CharSequence

List:接收方必須是ArrayList,里面的每個元素都必須能夠被AIDL支持

Map:接收方必須是HashMap,里面的每個元素都必須被AIDL支持

Parcelable:所有Parcelable接口的實現類,本質上是序列化與反序列化的過程

AIDL接口:所有AIDL接口本身也可作為可支持的數據類型,傳遞的是引用

需要注意的是:如果AIDL文件中使用到了自定義Parcelable對象和AIDL接口定義的對象,則必須要在所引用的AIDL文件中顯式import進來,不管它們是否和當前的AIDL文件位于同一個包內。

另一個需要注意的地方是:如果AIDL文件中用到自定義的Parcelable對象,則必須創建一個與它同名的AIDL文件,并在其中聲明該對象為Parcelable類型。

 

5. Content Provider(基于Binder)

ContentProvider是android中專門用于不同應用間進行數據共享的方式。和Messager一樣,底層同樣使用了Binder,由此可見,Binder在Android系統中是何等重要。

系統預制了很多ContentProvider,比如通訊錄信息、日程表信息等,要跨進程訪問這些信息,只需要通過ContentResolver的query、update、insert和delete方法即可。

 

6. Socket(網絡)

Socket位于傳輸層,是網絡上的兩個程序通過一個雙向的通信連接實現數據交換的一種進程通信方式。

socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供網絡開發所用的接口;HTTP是轎車,提供了封裝或顯示數據的具體形式;socket是發動機,提供了網絡通信的能力。

socket的使用是基于TCP或UDP協議。TCP面向連接,可靠穩定,雙向,有著經典的3次揮手、4次揮手,有丟包重發機制;UDP面向地址,不穩定,雙向,擁塞、復雜網絡環境可能會丟失,但效率更高。

補充:TCP用于在傳輸層有必要實現可靠傳輸的情況,由于它是面向連接并具有“順序控制”、重發控制等機制;而UDP則主要用于那些對高速傳輸和實時性有較高要求的通信或廣播通信。

因此TCP和UDP應該根據應用的目的按需使用,沒有絕對的優缺點。

 

最后引用一下Android開發藝術探索的總結,很全面啦~

 

通過對Android實現進程間通信方式的介紹,可以看出Binder的出鏡率很高,其中AIDL、Messager、ContentProvider都是基于Binder來實現的。可以說,Binder是Android最主要、也是最有特色的進程間通信的方式。

 

Binder是什么

直觀來說,Binder是android中的一個類,它實現了IBinder接口。

 

從IPC角度來看,Binder是android中一種跨進程通訊的方式。Binder可以理解為一種虛擬的物理設備,它的設備驅動是/dev/binder,該通訊方式在Linux中沒有。

從內存訪問的角度來看:Binder的作用就是讓兩個進程可以訪問同一塊內存空間,實現數據交換。

從Android應用層來看:Binder是客戶端和服務端進行通信的媒介。服務端會返回一個包含了服務端業務調用的Binder對象,然后客戶端通過這個Binder對象就可以獲取服務端提供的服務或數據。

從Android FrameWork角度來看:Binder是ServiceManager連接各種Manager和相應ManagerService的橋梁。

 

Binder采用的是Client-Server結構,客戶端進程通過獲取服務端進程的代理,并向代理接口方法中讀寫數據來完成進程間的通信。它的傳輸過程只需要一次拷貝,并為發送添加UID/PID身份,安全性較高。

對于Server而言,Binder可以看成是Server實現特定服務訪問的接入地址,Client通過訪問這個地址實現對Server的請求;對于Client而言,Binder可以看成是通向Server的管道入口,client要想和某個server通信,就必須先建立管道和管道入口。

  

Binder原理

從組件視角來說,Binder的通信框架包含Client、Server、Service Manager和Binder驅動。他們的關系類似與互聯網中:Server是服務器,Client是客戶終端,ServiceManager是域名服務器(DNS),Binder驅動是路由器。

 

說明:

① Service Manager用于管理系統中的各種服務

② Client、Server和Service Manager運行在用戶空間,Binder驅動運行在內核空間

③ Binder驅動和Service Manager在Android平臺中已經實現,可以看做是Android平臺的基礎架構,Client和Server是Android的應用層。開發人員只需自定義實現Client和Server,借助Android的基本平臺架構便可以直接進行IPC通信

④ Binder驅動程序提供設備文件/dev/binder與用戶空間交互,Client、Server和Service Manager通過open和ioctl文件操作函數與Binder驅動程序進行通信

⑤ Client和Server之間的進程間通信通過Binder驅動程序間接實現

⑥ Service Manager是整個Binder通信機制的大管家,是Android進程間通信機制Binder的守護進程,用來管理Server,并向Client提供查詢Server的接口

備注:無論是注冊服務和獲取服務的過程都需要Service Manager,需要注意的是此處的Service Manager是指Native層的Service Manager(C++),并非指framework層的Service Manager(Java)。

 

Binder的工作機制圖

 

  當客戶端發起遠程請求時,當前線程會被掛起直到服務端進程返回數據

  如果一個遠程方法是很耗時的,那么不能在UI線程中發起此遠程請求

  服務端的Binder方法是運行在Binder的線程池中的

  通過linkToDeath方法(對應unlinkToDeath)可以給Binder設置一個死亡代理,當Binder死亡時,能夠收到通知進而重新發起連接請求,恢復連接

 

BinderAIDL之間的關系

① Binder 可以簡單認為是 Android 特有的一種進程間通信方式,這種進程間通信方式的核心在于內核中的 Binder 驅動,該驅動負責數據的 IPC 雙向傳遞,向 Client 端提供訪問接口,并對 Server 端進行調用。

在 Android 中,Binder 通信也是可以支持傳遞“序列化”過的 Java 字節流的,但是它也只是將這串字節流當做普通數據類型來處理,它只負責傳遞,具體如何解釋這些數據流,需要有雙方約定,這個約定就是“序列化”和“反序列化”。

 

② AIDL 是為了使其他的應用程序也可以訪問本應用程序提供的 Service(這里的 Service 并不是特指 Android 四大組件中的Service,而是 Service 端可以向 Client 端提供的調用),Android 系統采用了 RPC 方式來實現。Android 使用一種 IDL 來公開服務的接口,這種能夠跨進程訪問的服務就被成為 AIDL 服務。AIDL 就是 Android 中眾多進程間通訊方式中的一種方式。

  

因此,網上給出的總結為:AIDL其實就一種接口的聲明規范,這里的接口指的是服務端提供的訪問接口,按照該規范寫出來的帶有aidl后綴的接口文件就是AIDL文件,通過編譯工具(aidl.exe)對AIDL文件進行編譯后,生成了可通過Binder IPC進行傳遞的binder數據類型(即Stub,該數據類型擴展了Binder,并實現了上述接口),用戶再通過擴展該Stub類,來定義服務端具體的、可供調用的功能。

 

 

參考文章

Android開發藝術探索

https://www.jianshu.com/p/fff0661ecbcd

https://www.jianshu.com/p/d1fac6ccee98

https://www.jianshu.com/p/8e4f9978e5b8

http://gityuan.com/2015/10/31/binder-prepare/

http://www.rzrgm.cn/sqchen/p/10660939.html

https://blog.csdn.net/qq_35770354/article/details/83046437

https://blog.csdn.net/hzw2017/java/article/details/81275438

https://blog.csdn.net/Tomasyb/java/article/details/72877958

https://blog.csdn.net/ding3106/java/article/details/83506819

https://blog.csdn.net/mijiaxiaojiu/java/article/details/101348324

https://blog.csdn.net/qq_36046305/article/details/84769349