- Android開發精要
- 范懷宇
- 1945字
- 2018-12-31 16:21:58
3.3.3 服務組件的進程間通信模型
當界面組件與服務組件綁定后,進程會通過方法的調用進行通信。而在實際應用中,前臺界面組件和后臺服務組件可能來自不同的應用,這就需要進行進程間通信。Android的進程間通信模型,主要包含三方面的內容。
Android的進程間通信模型架構
如圖3-9所示,Android的進程間通信模型和傳統的遠程通信模型非常相似,是典型的代理模式(Proxy Pattern)。在Android中,開發者將需要進行遠程通信的接口定義成android.os.IInterface接口的子類型(即圖中的MY_API)。該類型會有兩個實現者:用在調用者一端的MY_API.Proxy(簡稱為Proxy對象)和用在功能實現者一端的MY_ API.Stub(簡稱為Stub對象)。
在Proxy對象中,每個函數接口都有一個對應的指令值。當調用者調用這個接口時,Proxy會將數據和指令序列化成一個消息,發送到遠端的Stub對象。Stub對象會拆解出對應的指令和數據,并根據指令執行對應的邏輯,將結果返回給接口調用者,整個流程對于調用者而言完全透明。
Proxy數據包的發送和回傳工作,是通過android.os.Binder來實現的。在Binder對象中,有一個后臺消息循環線程,Proxy傳來的消息包會扔到消息隊列中等待解析和處理。Stub對象都是Binder的子類型,在服務端被實例化,其接口和實現與Proxy一一匹配,負責將Proxy傳遞過來的消息進行解包,得到其中的指令和數據。在實際應用中,每個Stub都會有一個實現子類Implement,實際上是由它來真正負責在Binder的后臺線程中執行所調用的功能。

圖3-9 Android的進程間通信模型
在界面組件和服務組件綁定的流程中,界面組件可以通過Context.bindService獲得IBinder的對象,通過它的靜態方法asInterface可以獲得Proxy對象實例。界面端使用Proxy對象,就可以實現對服務端功能的遠程調用。
對于IPC方法的調用是一個同步的流程,如果執行時間過長,就會阻塞調用方的線程,這時候需要用到異步IPC調用。如圖3-10所示,構造一個異步的IPC方法調用,需要傳入另一個IInterface對象。它的Stub對象(圖中的Callback.Stub)在調用組件,而它的Proxy對象(圖中的Callback.Proxy)會隨著序列化傳輸到服務組件,供服務端在操作執行完成后回調通知。服務組件完成操作后,會轉換角色扮演調用方,通過Callback.Proxy對象中的方法,將執行結果通知給調用組件。與第一次調用不同的是,這個調用通常是一個非阻塞性質的,即服務組件不等調用組件執行完成便立刻返回,以確保服務組件不會被阻塞,從而繼續為其他調用者服務。
框架代碼自動生成
通過前面介紹不難看出,整個進程通信模型中有固定的類型和方法需要實現,包含很多瑣碎而雷同的序列化與反序列化操作。如此機械的工作,當然需要通過自動化的手段來完成。在Android中,是通過AIDL(Android Interface Definition Language)的幫助來自動生成這些框架代碼的。

圖3-10 Android的進程間異步方法調用
AIDL是一種接口描述語言,它的語法取自Java,在參數上增加了輸入輸出方向的控制,一個簡單的AIDL文件示例如下:// 聲明Java包頭,該AIDL文件會生成對應的Java類,并放在gen目錄下package com.duguhome.test.SimpleAidl;
// 聲明導入的包,通常是另一個AIDL生成的類,用于異步調用函數import com.duguhome.test.SimpleAidl.ICallback;
// 接口聲明
interface IMyApi {
// 一個簡單的參數同步方法
int getId(in String name);
// 一個帶有輸入輸出參數的同步方法
void getIds(in String[] names, out Long[] values);
// 一個異步方法
void asyncGetId(in String name, in ICallback callback);};
Android SDK 中提供了AIDL的解析工具,它會根據所提供的AIDL文件,自動生成對應的MY_API、MY_API.Proxy、MY_API.Stub等類型的Java文件。開發者只需要繼承MY_API.Stub,實現Implement類型,填充真實的執行代碼即可,無需關注其他的底層通信細節,這大大提高了效率,降低了開發成本。
參數序列化
整個進程間通信的流程中,為了將一個進程中的數據傳遞到另一個進程中,還有一個重要的步驟,就是數據的序列化和反序列化—這是所有進程間通信的基礎。
在Android中,負責序列化和反序列化數據的是android.os.Parcel類,它提供了一系列的write和read接口,支持多種類型數據的序列化操作。Parcel支持的數據類型主要有三種。一種是基本數據和它們的列表、數組對象,比如int、long、double、List<String>、byte[]等;一種是實現了android.os.Parcelable接口的子類型對象—子類型通過派生writeToParcel方法和提供構造函數的途徑實現序列化相關的操作;此外,IBinder和IInterface的子類型對象也可以通過Parcel來序列化,這主要是為了支持服務綁定和異步方法調用。
Parcel序列化后的數據是齊位的二進制流,如此設計是為了效率而不是為了靈活性。這就意味著,寫入和讀取數據的順序與類型必須完全一致,稍有偏差,就會觸發異常。如果需要傳輸的數據隨著場景的不同有很大的變動,可以選擇使用android.os.Bundle對象,它會按照鍵值對的模式將信息寫入二進制數據流,以提高靈活性。
進程間通信機制是Android的重要基礎,被廣泛地應用在各類核心機制中,因此Parcel類型的實現主要通過C++來完成,上層Parcel對象通過JNI接口進行調用,從而提高了序列化相關操作的執行效率,確保Android系統可以高效運行。
小貼士
在Android開發中,我們時常需要通過Context.getSystemService接口來獲得指定的系統服務。這些系統服務,并不是通過服務組件來實現的。它們都位于系統的核心進程中,有獨立的線程空間。
通過Context.getSystemService獲得的,其實是這些服務的代理對象。這些對象會與真正的服務線程建立連接,通過IPC調用來實現對應的方法。與組件服務相比,系統服務的查詢和使用沒有利用Intent機制,雖然沒那么靈活,但更為直接簡便。
- Reporting with Visual Studio and Crystal Reports
- Learning RabbitMQ
- 深入淺出WPF
- 編寫高質量代碼:改善Python程序的91個建議
- UI智能化與前端智能化:工程技術、實現方法與編程思想
- Mastering JavaScript High Performance
- Clojure Reactive Programming
- 區塊鏈技術進階與實戰(第2版)
- Windows Embedded CE 6.0程序設計實戰
- Angular應用程序開發指南
- Python預測分析與機器學習
- Scrapy網絡爬蟲實戰
- Mapping with ArcGIS Pro
- Beginning PHP
- 輕松學Scratch 3.0 少兒編程(全彩)