AIDL 後端是產生虛設常式程式碼的目標。使用 AIDL 檔案時,一律須在特定執行階段以特定語言使用這些檔案。根據情境,您應該使用不同的 AIDL 後端。
在下表中,API 介面的穩定性是指根據這個 API 介面編譯程式碼,且程式碼可透過 system.img
libbinder.so
二進位檔獨立傳送。
AIDL 有下列後端:
後端 | 語言 | API 介面 | 建構系統 |
---|---|---|---|
Java | Java | SDK/SystemApi (穩定版*) | 全部 |
NDK | C++ | libbinder_ndk (穩定版*) | aidl_介面 |
單次通話成本 | C++ | libbinder (不穩定) | 全部 |
Rust | Rust | libbinder_rs (穩定*) | aidl_介面 |
- 這些 API 介面相當穩定,但許多 API (例如用於服務管理的 API) 會保留供內部平台使用,不適用於應用程式。如要進一步瞭解如何在應用程式中使用 AIDL,請參閱開發人員說明文件。
- Rust 後端是在 Android 12 中導入,NDK 後端自 Android 10 起已可供使用。
- Rust Crate 是以
libbinder_ndk
為基礎建構而成,因此具備穩定與可攜性。APEX 使用繫結器 Crate 的方式,與系統端的其他任何人相同。Rust 部分包含在 APEX 中並隨附於 APEX 中。依附於系統分區的libbinder_ndk.so
。
建構系統
視後端而定,有兩種方法可將 AIDL 編譯為虛設常式程式碼。如要進一步瞭解建構系統,請參閱 Soong 模組參考資料。
核心建構系統
在任何 cc_
或 java_
Android.bp 模組中 (或在 Android.mk
的同等模組中),可以將 .aidl
檔案指定為來源檔案。在此情況下,系統會使用 AIDL 的 Java/CPP 後端 (而非 NDK 後端),將使用對應 AIDL 檔案的類別自動新增至模組。local_include_dirs
等選項會指示建構系統在該模組的 aidl:
群組內,指定該模組中 AIDL 檔案的根路徑。請注意,Rust 後端只能與 Rust 搭配使用。rust_
模組的處理方式不同,因為系統不會將 AIDL 檔案指定為來源檔案。反之,aidl_interface
模組會產生名為 <aidl_interface name>-rust
的 rustlib
,以建立連結。詳情請參閱 Rust AIDL 範例。
aidl_介面
搭配這個建構系統使用的類型必須結構化。為了保持結構化, parcelable 必須直接包含欄位,且不得為直接以譯文語言定義的類型宣告。如需瞭解結構化 AIDL 如何與穩定的 AIDL 搭配運作,請參閱結構化與穩定的 AIDL。
類型
您可以將 aidl
編譯器視為類型的參照實作。建立介面時,請叫用 aidl --lang=<backend> ...
以查看產生的介面檔案。使用 aidl_interface
模組時,您可以在 out/soong/.intermediates/<path to module>/
中查看輸出內容。
Java/AIDL 類型 | C++ 類型 | NDK 類型 | Rust 類型 |
---|---|---|---|
布林值 | 布林值 | 布林值 | 布林值 |
位元組 | int8_t | int8_t | i8 |
char | 字元數 16_t | 字元數 16_t | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | F32 鍵 |
double | double | double | F64 |
字串 | android::String16 | std::字串 | 字串 |
android.os.Parcelable | android::Parcelable | 不適用 | 不適用 |
分身式 | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
二 [] | std::向量<T> | std::向量<T> | 傳入:&[T] 輸出:Vec<T> |
位元組 [] | std::向量<uint8_t> | std::vector<int8_t>1 | 傳入:&[u8] 輸出:Viec<u8> |
清單<T> | std::vector<T>2 | std::vector<T>3 | In: &[T]4 出局:Vec<T> |
檔案描述元 | android::base::unique_fd | 不適用 | binder::parcel::ParcelFileDescriptor |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
介面類型 (T) | android::sp<T> | std::shared_ptr<T>7 | binder::嚴格 |
parcelable 類型 (T) | 二 | 二 | 二 |
聯集類型 (T)5 | 二 | 二 | 二 |
T[N] 6 | std::陣列<T, N> | std::陣列<T, N> | [T;N] |
1. 在 Android 12 以上版本中,基於相容性因素,位元組陣列使用 uint8_t 而非 int8_t。
2. C++ 後端支援 List<T>
,其中 T
為 String
、IBinder
、ParcelFileDescriptor
或 parcelable。在 Android 13 以上版本中,T
可以是任何非原始類型 (包括介面類型) 但陣列除外。Android 開放原始碼計畫建議您使用 T[]
等陣列類型,因為它們適用於所有後端。
3. NDK 後端支援 List<T>
,其中 T
為 String
、ParcelFileDescriptor
或 Parcelable。在 Android 13 以上版本中,T
可以是陣列以外的任何非原始類型。
4. 視 Rust 程式碼的型別是輸入內容 (引數) 或輸出內容 (傳回值),系統會以不同方式傳遞類型。
5. Android 12 以上版本支援聯合類型。
6. Android 13 以上版本支援固定大小的陣列。固定大小的陣列可以有多個維度 (例如 int[3][4]
)。在 Java 後端,固定大小的陣列會以陣列類型表示。
7. 如要為繫結器 SharedRefBase
物件例項化,請使用 SharedRefBase::make\<My\>(... args ...)
。這個函式會建立 std::shared_ptr\<T\>
物件,如果繫結器由其他程序擁有,該物件同樣會在內部管理。透過其他方式建立物件會導致重複擁有權。
方向 (包含/傳出/傳出)
將引數類型指定至函式時,您可以將引數類型指定為 in
、out
或 inout
。這會控制傳送處理序間通訊 (IPC) 呼叫的方向資訊。in
是預設方向,表示資料已從呼叫端傳遞給受呼叫端。out
表示資料會從受呼叫端傳遞至呼叫端。inout
是這兩個項目的組合。不過,Android 團隊建議您避免使用引數指定碼 inout
。如果您將 inout
與版本化介面和舊版受呼叫者搭配使用,只有呼叫端中的其他欄位會重設為預設值。就 Rust 而言,一般 inout
類型會接收 &mut Vec<T>
,清單 inout
類型則會接收 &mut Vec<T>
。
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
UTF8/UTF16
透過 CPP 後端,您可以選擇字串為 utf-8 還是 utf-16。在 AIDL 中將字串宣告為 @utf8InCpp String
,即可自動將其轉換為 utf-8。NDK 和 Rust 後端一律使用 utf-8 字串。如要進一步瞭解 utf8InCpp
註解,請參閱「AIDL 中的註解」。
是否可為空值
您可以使用 @nullable
為 Java 後端中可為空值的類型加上註解,藉此向 CPP 和 NDK 後端公開空值。在 Rust 後端中,這些 @nullable
類型會公開為 Option<T>
。原生伺服器預設會拒絕空值。唯一的例外狀況是 interface
和 IBinder
類型,在 NDK 讀取和 CPP/NDK 寫入作業中,其一律為空值。如要進一步瞭解 nullable
註解,請參閱 AIDL 中的註解。
自訂 parcelable
「自訂 parcelable」是在目標後端中手動實作的 parcelable。請僅在為現有自訂 Parcelable 新增支援 (無法變更) 時,才使用自訂 parcelable。
為了宣告自訂 parcelable,讓 AIDL 知道其,AIDL parcelable 宣告如下所示:
package my.pack.age;
parcelable Foo;
根據預設,這會宣告 Java Parcelable,其中 my.pack.age.Foo
是實作 Parcelable
介面的 Java 類別。
如要在 AIDL 中宣告自訂 CPP 後端可位,請使用 cpp_header
:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
中的 C++ 實作如下所示:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
如要在 AIDL 中宣告自訂 NDK parcelable,請使用 ndk_header
:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
中的 NDK 實作如下所示:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
在 Android 15 (Android 開放原始碼計畫實驗功能) 中,如要在 AIDL 中宣告自訂 Rust 套件,請使用 rust_type
:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
中的 Rust 實作方式如下所示:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
接著,您可以將這個 parcelable 做為 AIDL 檔案的類型使用,但是 AIDL 不會產生。為 CPP/NDK 後端自訂 parcelable 提供 <
和 ==
運算子,以便在 union
中使用。
預設值
結構化 parcelable 可以宣告這些類型的基本、String
和陣列的每個欄位預設值。
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
在 Java 後端缺少預設值時,系統會將欄位初始化為原始類型為零,而如果是非原始類型,則會初始化為 null
。
在其他後端中,如果未定義預設值,系統就會以預設的初始化值初始化欄位。舉例來說,在 C++ 後端中,String
欄位會初始化為空白字串,List<T>
欄位則會初始化為空白 vector<T>
。@nullable
欄位會初始化為空值欄位。
處理錯誤
Android 作業系統提供內建的錯誤類型,讓服務在回報錯誤時使用。這些項目會由繫結器使用,任何實作繫結器介面的服務皆可使用。AIDL 定義已妥善記錄其使用行為,不需要任何使用者定義狀態或傳回類型。
有錯誤的輸出參數
當 AIDL 函式回報錯誤時,函式可能無法初始化或修改輸出參數。具體而言,如果錯誤發生在未封裝時發生,而不是在交易本身處理期間發生,輸出參數可能會遭到修改。一般來說,從 AIDL 函式取得錯誤時,所有 inout
和 out
參數以及傳回值 (如同某些後端中的 out
參數) 都應視為無限期。
要使用的錯誤值
許多 AIDL 介面都可以使用許多內建錯誤值,但有些值會以特殊的方式處理。舉例來說,當 EX_UNSUPPORTED_OPERATION
和 EX_ILLEGAL_ARGUMENT
說明錯誤狀況時可以使用,但 EX_TRANSACTION_FAILED
不得使用,因為基礎基礎架構將以特殊方式處理。如要進一步瞭解這些內建值,請查看後端專屬定義。
如果 AIDL 介面需要但內建錯誤類型未涵蓋的其他錯誤值,則可能會使用特殊的服務內建錯誤,加入使用者定義的服務專屬錯誤值。這些服務專屬的錯誤通常會在 AIDL 介面中定義為 const int
或 int
支援的 enum
,且不會由繫結器剖析。
在 Java 中,錯誤會對應至例外狀況,例如 android.os.RemoteException
。針對特定服務的例外狀況,Java 會使用 android.os.ServiceSpecificException
和使用者定義的錯誤。
Android 中的原生程式碼不使用例外狀況。CPP 後端會使用 android::binder::Status
。NDK 後端會使用 ndk::ScopedAStatus
。AIDL 產生的每個方法都會傳回其中一項,代表方法的狀態。Rust 後端會使用與 NDK 相同的例外狀況程式碼值,但會先將其轉換為原生 Rust 錯誤 (StatusCode
、ExceptionCode
),再傳送給使用者。如果是服務特定錯誤,傳回的 Status
或 ScopedAStatus
會搭配使用者定義的錯誤,一起使用 EX_SERVICE_SPECIFIC
。
內建錯誤類型位於下列檔案:
後端 | 定義 |
---|---|
Java | android/os/Parcel.java |
單次通話成本 | binder/Status.h |
NDK | android/binder_status.h |
Rust | android/binder_status.h |
使用各種後端
以下操作說明僅適用於 Android 平台程式碼。這些範例使用定義的類型 my.package.IFoo
。如要瞭解如何使用 Rust 後端,請參閱「Android Rust 模式」頁面上的「Rust AIDL 範例」。
匯入類型
無論定義的類型是介面、parcelable 或 聯集,您都可以在 Java 中匯入:
import my.package.IFoo;
或是在 CPP 後端中:
#include <my/package/IFoo.h>
或是在 NDK 後端中 (請注意額外的 aidl
命名空間):
#include <aidl/my/package/IFoo.h>
或是在 Rust 後端中:
use my_package::aidl::my::package::IFoo;
雖然您可以在 Java 中匯入巢狀類型,但在 CPP/NDK 後端中,您必須加入其根類型的標頭。舉例來說,匯入 my/package/IFoo.aidl
中定義的巢狀類型 Bar
(IFoo
是檔案的根類型) 時,您必須在 CPP 後端 (或 NDK 後端的 <aidl/my/package/IFoo.h>
) 中加入 <my/package/IFoo.h>
。
導入服務
如要實作服務,您必須繼承原生虛設常式類別。此類別會從繫結器驅動程式讀取指令,並執行您實作的方法。假設您有一個 AIDL 檔案:
package my.package;
interface IFoo {
int doFoo();
}
在 Java 中,您必須從此類別擴充:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
在 CPP 後端中:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
在 NDK 後端中 (請注意額外的 aidl
命名空間):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
在 Rust 後端中:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
或是使用非同步 Rust:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
註冊並取得服務
Android 平台中的服務通常會透過 servicemanager
程序註冊。除了下列 API 以外,某些 API 也會檢查服務 (這表示在服務無法使用時會立即傳回)。詳情請參閱對應的 servicemanager
介面。只有在針對平台 Android 進行編譯時,才能執行這些作業。
在 Java 中:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
在 CPP 後端中:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
在 NDK 後端中 (請注意額外的 aidl
命名空間):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
在 Rust 後端中:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
在非同步 Rust 後端,使用單一執行緒執行階段:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks will run on this thread.
std::future::pending().await
}
與其他選項的主要差異在於,使用非同步 Rust 和單一執行緒執行階段時,「不會」呼叫 join_thread_pool
。這是因為您需要為 Tokio 提供執行緒,讓 Tokio 能執行產生的工作。在本範例中,主執行緒將提供此目的。使用 tokio::spawn
產生的任何工作都會在主執行緒上執行。
在非同步 Rust 後端,使用多執行緒執行階段:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
使用多執行緒 Tokio 執行階段時,產生的工作不會在主執行緒上執行。因此,在主執行緒上呼叫 join_thread_pool
比較合理,讓主執行緒不是處於閒置狀態。您必須將呼叫納入 block_in_place
,以便離開非同步環境。
死亡連結
你可以要求在代管繫結機制的服務終止時收到通知。這有助於避免回呼 Proxy 外洩,或協助修正錯誤。對繫結器 Proxy 物件發出這些呼叫。
- 在 Java 中,請使用
android.os.IBinder::linkToDeath
。 - 在 CPP 後端中,使用
android::IBinder::linkToDeath
。 - 在 NDK 後端中,使用
AIBinder_linkToDeath
。 - 在 Rust 後端中,建立
DeathRecipient
物件,然後呼叫my_binder.link_to_death(&mut my_death_recipient)
。請注意,由於DeathRecipient
擁有回呼,因此您也必須持續讓該物件保持活躍狀態,而您要接收通知。
來電者資訊
收到核心繫結器呼叫時,呼叫端資訊會在數個 API 中提供。PID (或程序 ID) 是指傳送交易的程序 Linux 程序 ID。UID (或使用者 ID) 是指 Linux 使用者 ID。收到單向呼叫時,呼叫 PID 為 0。在繫結器交易結構定義之外,這些函式會傳回目前程序的 PID 和 UID。
在 Java 後端中:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
在 CPP 後端中:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
在 NDK 後端中:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
在 Rust 後端中,實作介面時請指定下列項目 (而非允許預設值):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
針對服務產生的錯誤報告和偵錯 API
執行錯誤報告時 (例如使用 adb bugreport
),系統會從系統各處收集相關資訊,以協助偵錯各種問題。如果是 AIDL 服務,錯誤報告會在所有向服務管理員註冊的服務中使用二進位檔 dumpsys
,以便將資訊轉儲到錯誤報告中。您也可以在指令列中使用 dumpsys
,透過 dumpsys SERVICE [ARGS]
從服務取得資訊。在 C++ 和 Java 後端中,您可以使用 addService
的額外引數來控制服務的轉儲順序。您也可以使用 dumpsys --pid SERVICE
在偵錯時取得服務的 PID。
如要為服務新增自訂輸出內容,可以覆寫伺服器物件中的 dump
方法,就像實作 AIDL 檔案中定義的任何其他 IPC 方法一樣。執行此操作時,您應限制傾印至應用程式權限 android.permission.DUMP
,或限制傾印至特定 UID。
在 Java 後端中:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
在 CPP 後端中:
status_t dump(int, const android::android::Vector<android::String16>&) override;
在 NDK 後端中:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
在 Rust 後端中,實作介面時請指定下列項目 (而非允許預設值):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
動態取得介面描述元
介面描述元可識別介面類型。在偵錯或有不明的繫結器時,這非常實用。
在 Java 中,您可以透過程式碼取得介面描述元,例如:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
在 CPP 後端中:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
NDK 和 Rust 後端不支援這項功能。
靜態取得介面描述元
有時候 (例如註冊 @VintfStability
服務時),您必須知道介面描述元是靜態的情況。在 Java 中,您可以新增如下程式碼來取得描述元:
import my.package.IFoo;
... IFoo.DESCRIPTOR
在 CPP 後端中:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
在 NDK 後端中 (請注意額外的 aidl
命名空間):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
在 Rust 後端中:
aidl::my::package::BnFoo::get_descriptor()
列舉範圍
在原生後端中,您可以疊代列舉可以接管的可能值。由於程式碼大小考量,Java 並未支援此做法。
針對 AIDL 中定義的列舉 MyEnum
,疊代提供方式如下。
在 CPP 後端中:
::android::enum_range<MyEnum>()
在 NDK 後端中:
::ndk::enum_range<MyEnum>()
在 Rust 後端中:
MyEnum::enum_values()
執行緒管理
程序中的每個 libbinder
執行個體都維護一個執行緒集區。在大多數情況下,這應為一個執行緒集區,並可在所有後端共用。唯一的例外狀況是供應商程式碼可能會載入另一個 libbinder
副本來與 /dev/vndbinder
通訊。由於這是在單獨的繫結器節點上,因此執行緒集區不會共用。
如果是 Java 後端,執行緒集區只能增加大小 (因為已經啟動):
BinderInternal.setMaxThreads(<new larger value>);
CPP 後端可執行下列作業:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
同樣地,在 NDK 後端中:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
在 Rust 後端中:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
使用非同步 Rust 後端時,需要兩個執行緒集區:繫結器和 Tokio。這表示使用非同步 Rust 的應用程式需要特別注意,特別是使用 join_thread_pool
時。詳情請參閱「註冊服務」一節。
預留名稱
C++、Java 和 Rust 會保留部分名稱做為關鍵字或用於特定語言。雖然 AIDL 不會根據語言規則強制執行限制,但使用與保留名稱相符的欄位或類型名稱可能會導致 C++ 或 Java 的編譯失敗。如果是 Rust,請使用「原始 ID」語法重新命名欄位或類型,可使用 r#
前置字串存取。
建議您盡量避免在 AIDL 定義中使用保留名稱,以免發生非人為繫結或外部編譯失敗的問題。
如果 AIDL 定義中已有保留名稱,您可以放心重新命名欄位,同時保持通訊協定相容。您可能需要更新程式碼才能繼續建構,但任何已經建構的程式會繼續互通。
應避免的名稱: