W Androidzie 8.0 zmieniliśmy architekturę systemu, aby wyraźnie oddzielić platformę Androida od kodu związanego z konkretnym urządzeniem lub dostawcą. Android zdefiniował już wiele takich interfejsów w postaci interfejsów HAL, zdefiniowanych jako nagłówki C w pliku hardware/libhardware
. HIDL
zastąpił te interfejsy HAL stabilnymi interfejsami z wersją, które mogą być w języku Java (opisane poniżej) lub interfejsami HIDL po stronie klienta i serwera w języku C++.
Interfejsy HIDL są przeznaczone głównie do używania w kodzie natywnym, dlatego HIDL koncentruje się na automatycznym generowaniu wydajnego kodu w C++. Jednak interfejsy HIDL muszą być też dostępne do użycia bezpośrednio z Java, ponieważ niektóre podsystemy Androida (np. telefonia) mają interfejsy HIDL w Java.
Strony w tej sekcji opisują interfejs Java dla interfejsów HIDL, podają szczegółowe informacje o tworzeniu, rejestrowaniu i używaniu usług oraz wyjaśniają, jak interfejsy HAL i klienci HAL napisane w języku Java współdziałają z systemem HIDL RPC.
Przykład klienta
Oto przykład klienta dla interfejsu IFoo
w pakiecie android.hardware.foo@1.0
, który jest zarejestrowany jako nazwa usługi default
i usługi dodatkowej o niestandardowej nazwie second_impl
.
Dodawanie bibliotek
Jeśli chcesz z niego korzystać, musisz dodać zależności od odpowiedniej biblioteki stub HIDL. Zwykle jest to biblioteka statyczna:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
Jeśli wiesz, że już używasz tych bibliotek, możesz też użyć wspólnego linkowania:
// in Android.bp libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
Dodatkowe kwestie dotyczące dodawania bibliotek w Androidzie 10
Jeśli masz aplikację systemową lub aplikację dostawcy przeznaczoną na Androida 10 lub nowszego, możesz statycznie uwzględnić te biblioteki. Możesz też używać (tylko) klas HIDL z niestandardowych plików JAR zainstalowanych na urządzeniu za pomocą stabilnych interfejsów Java API udostępnionych za pomocą dotychczasowego mechanizmu uses-library
dla aplikacji systemowych. Drugie podejście pozwala zaoszczędzić miejsce na urządzeniu. Więcej informacji znajdziesz w artykule Implementowanie biblioteki Java SDK. W przypadku starszych aplikacji zachowane jest stare działanie.
Począwszy od Androida 10 dostępne są też wersje „płytkie” tych bibliotek. Obejmują one klasę, której dotyczy problem, ale nie obejmują żadnych klas zależnych. Na przykład:
android.hardware.foo-V1.0-java-shallow
zawiera klasy w pakiecie foo, ale nie zawiera klas w android.hidl.base-V1.0-java
, który zawiera klasę podstawową wszystkich interfejsów HIDL. Jeśli tworzysz bibliotekę, która zawiera już preferowane klasy podstawowe interfejsu dostępne jako zależności, możesz użyć:
// 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
Biblioteki bazowe i biblioteki menedżera HIDL nie są też już dostępne w klasie ścieżki uruchamiania aplikacji (wcześniej były one czasami używane jako ukryte interfejsy API ze względu na mechanizm Androida polegający na tym, że najpierw ładuje się zastępnik). Zamiast tego zostały przeniesione do nowej przestrzeni nazw z jarjar
, a aplikacje, które z nich korzystają (koniecznie aplikacje prywatne), muszą mieć ich osobne kopie. Moduł na ścieżce ładowania klas używający HIDL musi używać płytkich wersji tych bibliotek Java i dodawać jarjar_rules: ":framework-jarjar-rules"
do Android.bp
, aby używać wersji tych bibliotek, która istnieje na ścieżce ładowania klas.
Modyfikowanie kodu źródłowego w Javie
Ta usługa ma tylko jedną wersję (@1.0
), więc ten kod pobiera tylko tę wersję. Informacje o obsługiwaniu wielu różnych wersji usługi znajdziesz w rozszerzeniach interfejsu.
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(…);
Świadczenie usług
Kod frameworka w Javie może wymagać interfejsów do odbierania asynchronicznych wywołań zwrotnych z HAL-i.
W przypadku interfejsu IFooCallback
w wersji 1.0 pakietu android.hardware.foo
możesz go zaimplementować w języku Java w ten sposób:
- Zdefiniuj interfejs w HIDL.
- Otwórz
/tmp/android/hardware/foo/IFooCallback.java
jako odniesienie. - Utwórz nowy moduł dla implementacji w Javie.
- Zapoznaj się z klasą abstrakcyjną
android.hardware.foo.V1_0.IFooCallback.Stub
, a potem napisz nową klasę, aby ją rozszerzyć i zaimplementować metody abstrakcyjne.
Wyświetlanie plików wygenerowanych automatycznie
Aby wyświetlić automatycznie wygenerowane pliki, uruchom:
hidl-gen -o /tmp -Ljava \ -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0
Te polecenia generują katalog
/tmp/android/hardware/foo/1.0
. W przypadku pliku hardware/interfaces/foo/1.0/IFooCallback.hal
generuje plik /tmp/android/hardware/foo/1.0/IFooCallback.java
, który zawiera interfejs Java, kod serwera proxy i stuby (zarówno serwer proxy, jak i stuby są zgodne z interfejsem).
-Lmakefile
generuje reguły, które wykonują to polecenie w czasie kompilacji, i pozwalają uwzględnić android.hardware.foo-V1.0-java
oraz połączyć odpowiednie pliki. Skrypt, który automatycznie wykonuje tę czynność w przypadku projektu zawierającego wiele interfejsów, znajdziesz na stronie hardware/interfaces/update-makefiles.sh
.
Ścieżki w tym przykładzie są względne; sprzęt/interfejsy mogą być tymczasowym katalogiem w drzewie kodu, aby umożliwić opracowanie interfejsu HAL przed jego opublikowaniem.
Uruchamianie usługi
HAL udostępnia interfejs IFoo
, który musi wywoływać asynchroniczne callbacki do platformy za pomocą interfejsu IFooCallback
. Interfejs IFooCallback
nie jest rejestrowany według nazwy jako usługa możliwa do znalezienia. Zamiast tego interfejs IFooCallback
musi zawierać metodę, taką jak setFooCallback(IFooCallback x)
.IFoo
Aby skonfigurować IFooCallback
z wersji 1.0 pakietu android.hardware.foo
, dodaj android.hardware.foo-V1.0-java
do Android.mk
. Kod służący do uruchamiania usługi:
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);
Rozszerzenia interfejsu
Zakładając, że dana usługa implementuje interfejs IFoo
na wszystkich urządzeniach, możliwe jest, że na danym urządzeniu usługa może udostępniać dodatkowe funkcje zaimplementowane w rozszerzeniu interfejsu IBetterFoo
, jak np.:
interface IFoo { ... }; interface IBetterFoo extends IFoo { ... };
Kod wywołujący, który jest świadomy rozszerzonego interfejsu, może użyć metody Java castFrom()
, aby bezpiecznie zmienić interfejs podstawowy na interfejs rozszerzony:
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. }