官术网_书友最值得收藏!

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 AIDL的具體構造和使用,可以參見SDK文檔:http://developer.android.com/guide/developing/ tools/aidl.html。

參數序列化

整個進程間通信的流程中,為了將一個進程中的數據傳遞到另一個進程中,還有一個重要的步驟,就是數據的序列化和反序列化—這是所有進程間通信的基礎。

在Android中,負責序列化和反序列化數據的是android.os.Parcel類,它提供了一系列的write和read接口,支持多種類型數據的序列化操作。Parcel支持的數據類型主要有三種。一種是基本數據和它們的列表、數組對象,比如int、long、double、List<String>、byte[]等;一種是實現了android.os.Parcelable接口的子類型對象—子類型通過派生writeToParcel方法和提供構造函數的途徑實現序列化相關的操作一個簡單的實例參見SDK文檔:http://developer.android.com/reference/android/os/Parcelable.html。;此外,IBinder和IInterface的子類型對象也可以通過Parcel來序列化,這主要是為了支持服務綁定和異步方法調用。

Parcel序列化后的數據是齊位的二進制流,如此設計是為了效率而不是為了靈活性。這就意味著,寫入和讀取數據的順序與類型必須完全一致,稍有偏差,就會觸發異常。如果需要傳輸的數據隨著場景的不同有很大的變動,可以選擇使用android.os.Bundle對象,它會按照鍵值對的模式將信息寫入二進制數據流,以提高靈活性。

進程間通信機制是Android的重要基礎,被廣泛地應用在各類核心機制中,因此Parcel類型的實現主要通過C++來完成,上層Parcel對象通過JNI接口進行調用,從而提高了序列化相關操作的執行效率,確保Android系統可以高效運行。

小貼士

在Android開發中,我們時常需要通過Context.getSystemService接口來獲得指定的系統服務。這些系統服務,并不是通過服務組件來實現的。它們都位于系統的核心進程中,有獨立的線程空間。

通過Context.getSystemService獲得的,其實是這些服務的代理對象。這些對象會與真正的服務線程建立連接,通過IPC調用來實現對應的方法。與組件服務相比,系統服務的查詢和使用沒有利用Intent機制,雖然沒那么靈活,但更為直接簡便。

主站蜘蛛池模板: 淅川县| 罗山县| 洛浦县| 阿城市| 甘肃省| 海兴县| 民勤县| 墨江| 达日县| 新疆| 濮阳县| 丽水市| 陇南市| 丰都县| 祥云县| 双城市| 新闻| 古蔺县| 济源市| 博客| 平舆县| 黔西| 安化县| 斗六市| 上虞市| 乐都县| 仙游县| 赫章县| 旬邑县| 思茅市| 泾阳县| 合阳县| 阿拉善左旗| 县级市| 呼玛县| 景洪市| 宜兰县| 河北省| 应用必备| 太谷县| 肥东县|