在 Android 8.0 中,Android 作業系統已重新架構,以便在裝置獨立的 Android 平台與裝置和供應商專屬程式碼之間定義明確的介面。Android 已以 HAL 介面形式定義許多這類介面,並在 hardware/libhardware
中定義為 C 標頭。HIDL 將這些 HAL 介面替換為穩定的版本介面,這些介面可以是 Java 介面 (如下所述),也可以是 C++ 中的用戶端和伺服器端 HIDL 介面。
HIDL 介面主要用於原生程式碼,因此 HIDL 著重於在 C++ 中自動產生高效程式碼。不過,HIDL 介面也必須可直接從 Java 使用,因為某些 Android 子系統 (例如 Telephony) 具有 Java HIDL 介面。
本節的頁面說明 HIDL 介面的 Java 前端,詳細說明如何建立、註冊及使用服務,並說明以 Java 編寫的 HAL 和 HAL 用戶端如何與 HIDL RPC 系統互動。
用戶端範例
這是範例,說明包裝函式 android.hardware.foo@1.0
中介面 IFoo
的用戶端,已註冊為服務名稱 default
,以及使用自訂服務名稱 second_impl
的額外服務。
新增程式庫
如要使用對應的 HIDL 虛設程式庫,您必須新增依附元件。通常是靜態資料庫:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
如果您知道自己已提取這些程式庫的相依項目,也可以使用共用連結:
// in Android.bp libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
在 Android 10 中新增程式庫的其他考量
如果您有指定 Android 10 以上版本為目標版本的系統或供應商應用程式,可以靜態加入這些程式庫。您也可以使用 (僅) 從裝置上安裝的自訂 JAR 檔案使用 HIDL 類別,並透過現有的 uses-library
機制為系統應用程式提供穩定的 Java API。後者可節省裝置空間。詳情請參閱「實作 Java SDK 程式庫」。對於較舊的應用程式,系統會保留舊的行為。
從 Android 10 開始,這些程式庫也提供「淺層」版本。這些類別包括有問題的類別,但不包括任何依附類別。舉例來說,android.hardware.foo-V1.0-java-shallow
會納入 foo 套件中的類別,但不會納入 android.hidl.base-V1.0-java
中的類別,後者包含所有 HIDL 介面的基礎類別。如果您建立的程式庫已將偏好的介面基本類別設為依附元件,可以使用以下方式:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java-shallow", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow
應用程式的啟動 classpath 也無法再使用 HIDL 基礎和管理程式庫 (先前由於 Android 的委派第一類載入程式,因此有時會將這些做為隱藏 API 使用)。而是已移至含有 jarjar
的新命名空間,而使用這些 (必須是私密應用程式) 的應用程式必須擁有各自的獨立副本。使用 HIDL 的啟動類別路徑中的模組,必須使用這些 Java 程式庫的淺層變化版本,並將 jarjar_rules: ":framework-jarjar-rules"
新增至其 Android.bp
,以便使用啟動類別路徑中存在的這些程式庫版本。
修改 Java 來源
這個服務只有一個版本 (@1.0
),因此這段程式碼只會擷取該版本。如要瞭解如何處理多個不同版本的服務,請參閱介面擴充功能。
import android.hardware.foo.V1_0.IFoo; ... // retry to wait until the service starts up if it is in the manifest IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IFoo anotherServer = IFoo.getService("second_impl", true /* retry */); server.doSomething(…);
提供服務
Java 中的架構程式碼可能需要提供介面,才能接收來自 HAL 的非同步回呼。
針對 android.hardware.foo
套件 1.0 版中的 IFooCallback
介面,您可以按照下列步驟在 Java 中實作介面:
- 在 HIDL 中定義介面。
- 開啟
/tmp/android/hardware/foo/IFooCallback.java
做為參考。 - 為 Java 實作建立新模組。
- 檢查抽象類別
android.hardware.foo.V1_0.IFooCallback.Stub
,然後編寫新的類別來擴充該類別,並實作抽象方法。
查看自動產生的檔案
如要查看自動產生的檔案,請執行:
hidl-gen -o /tmp -Ljava \ -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0
這些指令會產生目錄 /tmp/android/hardware/foo/1.0
。針對檔案 hardware/interfaces/foo/1.0/IFooCallback.hal
,這會產生檔案 /tmp/android/hardware/foo/1.0/IFooCallback.java
,該檔案會封裝 Java 介面、Proxy 程式碼和 Stub (Proxy 和 Stub 都符合介面)。
-Lmakefile
會產生規則,在建構期間執行此指令,並允許您加入 android.hardware.foo-V1.0-java
,並連結至適當的檔案。hardware/interfaces/update-makefiles.sh
提供的指令碼可自動為充滿介面的專案執行這項操作。本例中的路徑為相對路徑;「hardware/interfaces」可以是程式碼樹狀結構底下的臨時目錄,方便您在發布 HAL 前先行開發。
執行服務
HAL 會提供 IFoo
介面,該介面必須透過 IFooCallback
介面向架構發出非同步回呼。IFooCallback
介面並未以名稱註冊為可偵測的服務;IFoo
必須包含 setFooCallback(IFooCallback x)
等方法。
如要從 android.hardware.foo
套件的 1.0 版設定 IFooCallback
,請將 android.hardware.foo-V1.0-java
新增至 Android.mk
。執行服務的程式碼如下:
import android.hardware.foo.V1_0.IFoo; import android.hardware.foo.V1_0.IFooCallback.Stub; .... class FooCallback extends IFooCallback.Stub { // implement methods } .... // Get the service from which you will be receiving callbacks. // This also starts the threadpool for your callback service. IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available .... // This must be a persistent instance variable, not local, // to avoid premature garbage collection. FooCallback mFooCallback = new FooCallback(); .... // Do this once to create the callback service and tell the "foo-bar" service server.setFooCallback(mFooCallback);
介面擴充功能
假設特定服務在所有裝置上實作 IFoo
介面,在特定裝置上,服務可能會提供在介面擴充功能 IBetterFoo
中實作的其他功能,如下所示:
interface IFoo { ... }; interface IBetterFoo extends IFoo { ... };
呼叫瞭解擴充介面的程式碼可使用 castFrom()
Java 方法,將基本介面安全地轉換為擴充介面:
IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IBetterFoo extendedService = IBetterFoo.castFrom(baseService); if (extendedService != null) { // The service implements the extended interface. } else { // The service implements only the base interface. }