當前位置:
首頁 > 最新 > Android AIDL淺析及非同步使用

Android AIDL淺析及非同步使用

AIDL:Android Interface Definition Language,即 Android 介面定義語言。

AIDL 是什麼

Android 系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。

為了使其他的應用程序也可以訪問本應用程序提供的服務,Android 系統採用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其他的基於 RPC 的解決方案一樣,Android 使用一種介面定義語言(Interface Definition Language,IDL)來公開服務的介面。我們知道 Android 四大組件中的 3 種(Activity、BroadcastReceiver和ContentProvider)都可以進行跨進程訪問,另外一種 Android 組件 Service 同樣可以。因此,可以將這種可以跨進程訪問的服務稱為 AIDL(Android Interface Definition Language)服務。

在介紹 AIDL 的使用以及其它特性前,我們先來了解下 AIDL 的核心——Binder

Android 的 Binder 機制淺析

看過一些關於 Binder 的文章,總得來說 Binder 機制的底層實現很複雜,相當複雜,要完全搞清楚,得花大量的時間。從某種角度來說,個人覺得,對於 Binder,我們只需要了解其上層原理以及使用方法即可。

直觀來看,從代碼的角度來說,Binder 是 Android 系統源碼中的一個類,它實現了 IBinder 介面;從 IPC 角度來說,Binder 是 Android 中的一種跨進程通信方式;從 Android Framework 角度來講,Binder 是 ServiceManager 連接各種 Manager(ActivityManager、WindowManager 等等)和相應 ManagerService 的橋樑;從 Android 應用層來說,Binder 是客戶端和服務端進行通信的媒介,當 bindService 的時候,服務端會返回一個包含了服務端業務調用的 Binder 對象,通過這個 Binder 對象,客戶端就可以和服務端進行通信,這裡的服務包括普通服務和基於 AIDL 的服務。

接下來,我們通過一個 AIDL 示例,來分析 Binder 的工作機制。在工程目錄中新建一個名為 aidl 的 package,然後新建 Book.Java、Book.aidl(創建此文件時,as 會提示已存在,需要先用其它命令,創建成功後再重命名為 Book.aidl )和 IBookManager.aidl,代碼如下:

這是系統生成的 Binder 類,接下來我們要利用這個類來分析 Binder 的工作原理。其代碼如下:(生成的代碼格式很亂,可以格式化代碼之後看)

可見,系統為我們生成了一個 IBookManager 介面,它繼承了 IInterface 這個介面,所以這裡要注意下,所有可以在 Binder 中傳輸的介面,都需要繼承 IInterface 介面。

接下來分析下,該類的工作機制。仔細看,可以發現,該類主要分成三個部分:

定義自身方法( getBookList 方法和 addBook 方法);

內部靜態類-Stub,該類繼承 Binder,同時實現 IBookManager 介面

Stub的內部代理類-Proxy,也實現 IBookManager 介面

第一部分我們不用管它,主要看 Stub 類和 Proxy 類。在 Stub 中,首先聲明了兩個用於標識 IBookManager 方法的整型變數,這兩個變數用於標識在 transact 過程中客戶端所請求的是哪個方法。接著,是 asInterface 方法,該方法用於將服務端的 Binder 對象轉換成客戶端所需的 AIDL 介面類型的對象,該方法通過調用 Binder 的 queryLocalInterface 方法,判斷客戶端和服務端是否處於同一進程,如果客戶端、服務端處於同一進程,那麼此方法直接返回服務端的 Stub,否則,返回 Stub 的代理對象 Proxy。queryLocalInterface 實現如下:

mOwner 是在 Stub 構造函數中傳進去的 this 參數。

接下來是 Stub 的代理類 Stub.Proxy,由上面的分析可知,Stub.Proxy 是運行在客戶端的(由 asInterface 方法返回給客戶端的對象),Stub.Proxy對象創建後,持有服務端的 Binder 對象,用於客戶端請求時調用服務端方法進行遠程調用。客戶端在向服務端發起請求時,調用 Stub.Proxy 的相應方法,Stub.Proxy 方法的流程如下:

首先創建該方法所需要的輸入型 Parcel 對象 _data、輸出型對象 _reply 和返回值對象(如果有);

然後把該方法的參數信息寫入 _data 中(如果有);

接著調用 transact 方法進行 RPC(遠程過程調用)請求,同時當前線程掛起;

然後在 transact 方法通過服務端的 Binder 對象調用服務端的 onTransact 方法,即 Stub 中的 onTransact 方法;

onTransact 方法返回後,當前線程繼續執行,並從 _reply 中取出 RPC 過程返回的結果;

最後返回 _reply 中的數據(如果客戶端請求方法需要返回值)。

以上,就是系統生成的 IBookManager 的工作過程,需要注意下,服務端的 onTransact 方法是運行在 Binder 線程池中的。由於 IBookManager.Stub 類繼承 Binder,所以上述分析即 Binder 的工作機制,簡單總結下:

在客戶端和服務端連接時(一般通過 bindService 方法),服務端通過 asInterface 方法給客戶端返回一個 IInterface 介面類型的對象(如果客戶端和服務端在同一個進程,則返回服務端的 Binder 對象,否則返回 服務端 Binder 對象的代理);

客戶端通過該對象向服務端進行請求,如果客戶端和服務端不在同一個進程,則通過該對象所代理的 Binder 對象進行 RPC 請求,否則,直接通過 Binder 對象調用服務端相應方法。

或者參考下圖理解下:

由此可見,Binder 在 AIDL 中承載著重要的職能,是 AIDL 的核心,理解了 Binder 的工作機制,其實在很多方面都很有用。

AIDL 的使用

一套 AIDL 服務搭建的步驟如下:

創建 .aidl 文件,系統生成相應的繼承 IInterface 的介面類(暴露給客戶端的介面)。

創建一個 Service(服務端),實現 .aidl 文件中的介面。

創建客戶端,綁定服務端的 Service。

客戶端綁定服務端成功後,將服務端返回的 Binder 對象轉成 AIDL 介面所屬的 IInterface 類型。調用 AIDL 中的方法,實現和服務端的通信。

接下來,我們使用上面的 IBookManager 來實現 AIDL。

創建 .aidl 文件

直接使用上面創建好的 Book.aidl、IBookManager.aidl 文件即可

創建服務端

創建一個 Service,命名為 BookManagerService,代碼如下:

然後在 AndroidManifest 中註冊 Service,注意啟動多進程:

客戶端綁定服務端的 Service

客戶端的創建,直接使用 Activity 即可,綁定遠程服務的代碼如下:

與服務端通信

與伺服器綁定成功後,首先在 ServiceConnection 的回調中,將服務端返回的 Binder 對象轉換成 AIDL 介面所屬的對象,就可以調用相應方法和服務端通信了,代碼如下:

代碼中,我們先查詢了服務端的圖書列表,接著向服務端添加一本書,然後再次查詢看是否添加成功。運行下看 log,如下圖所示:

可見,運行結果和預期結果一致。完整的客戶端代碼如下:

到此,一個簡單的 AIDL 示例就完成了,當然,AIDL 的使用遠沒有那麼簡單,還有很多情景需要考慮的,比如:客戶端需要隨時服務端在狀態變化時同時客戶端,類似觀察這模式,那麼訂閱與反訂閱怎麼實現;Binder 意外死亡,怎麼重連等等,更多內容,可以參考《Android藝術開發探索》,電子書下載

AIDL 的非同步調用

AIDL 的調用過程是同步還是非同步的?

這個問題其實看這一節的標題大家都知道了,AIDL 的調用過程是同步的。同時,上面分析 Binder 的機制時,也提到了,客戶端進行遠程 RPC 請求時,線程會掛起,等待結果,由此也可知,AIDL 的調用過程是同步的,下面來驗證下。

首先,在服務端的 BookManagerService 中實現的 Binder 對象的 getBookList 方法添加延時執行,如下圖所示:

然後,在客戶端的 BookManagerActivity 中添加一個按鈕,點擊按鈕時調用服務端 Binder 的 getBookList 做 RPC 請求。代碼如下:

運行後,連續點擊按鈕,結果如下圖所示:

由圖所知,連續點擊按鈕過後一段時間,出現了無響應錯誤( ANR ),由此可知,客戶端向服務端做 RPC 請求時,是同步的,也就是說:AIDL 的調用過程是同步的

AIDL 的調用過程是同步的,當我們需要服務端做耗時操作時,肯定是不能使用同步調用的,否則輕者影響用戶體驗,重者直接 ANR 或者應用崩潰。那麼如何使 AIDL 的調用過程是非同步的呢?

其實也很簡單,只需要把調用放到非 UI 線程即可,如果要對調用的返回做 UI 更新的話,再通過 Handler 處理即可。如下圖所示:

本文參考

《Android開發藝術探索》 第二章

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 開發技能總結 的精彩文章:

TAG:開發技能總結 |