AIDL 後端是存根程式碼產生的目標。使用 AIDL 檔案時,您始終以具有特定執行時間的特定語言使用它們。根據上下文,您應該使用不同的 AIDL 後端。
在下表中,API 表面的穩定性是指針對此 API 表面編譯程式碼的能力,使得程式碼可以獨立於system.img
libbinder.so
二進位檔案進行交付。
AIDL 有以下後端:
後端 | 語言 | API表面 | 建構系統 |
---|---|---|---|
爪哇 | 爪哇 | SDK/SystemApi(穩定*) | 全部 |
NDK | C++ | libbinder_ndk(穩定*) | 輔助介面 |
消費者保護計劃 | C++ | libbinder(不穩定) | 全部 |
鏽 | 鏽 | libbinder_rs(穩定*) | 輔助介面 |
- 這些 API 表面是穩定的,但許多 API(例如用於服務管理的 API)保留供內部平台使用,且不可用於應用程式。有關如何在應用程式中使用 AIDL 的更多信息,請參閱開發人員文件。
- Rust 後端在 Android 12 中引入; NDK 後端從 Android 10 開始可用。
- Rust 箱建立在
libbinder_ndk
之上,這使得它穩定且可移植。 APEX 使用活頁夾板條箱的方式與系統端其他任何人的方式相同。 Rust 部分捆綁到 APEX 中並在其中發貨。它取決於系統分割區上的libbinder_ndk.so
。
建構系統
根據後端的不同,有兩種方法可以將 AIDL 編譯為存根程式碼。有關建置系統的更多詳細信息,請參閱Soong Module Reference 。
核心建構系統
在任何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 範例。
輔助介面
此建構系統使用的類型必須是結構化的。為了結構化,parcelables 必須直接包含字段,而不是直接在目標語言中定義的類型聲明。有關結構化 AIDL 如何與穩定 AIDL 結合,請參閱結構化 AIDL 與穩定 AIDL 。
類型
您可以將aidl
編譯器視為類型的參考實作。建立介面時,呼叫aidl --lang=<backend> ...
以查看產生的介面檔案。當您使用aidl_interface
模組時,您可以在out/soong/.intermediates/<path to module>/
中查看輸出。
Java/AIDL 類型 | C++ 類型 | NDK類型 | 銹型 |
---|---|---|---|
布林值 | 布林值 | 布林值 | 布林值 |
位元組 | int8_t | int8_t | i8 |
字元 | 字元16_t | 字元16_t | u16 |
整數 | int32_t | int32_t | i32 |
長的 | int64_t | int64_t | i64 |
漂浮 | 漂浮 | 漂浮 | F32 |
雙倍的 | 雙倍的 | 雙倍的 | f64 |
細繩 | 機器人::String16 | 標準::字串 | 細繩 |
android.os.Parcelable | android::可打包 | 不適用 | 不適用 |
綁定器 | android::IBinder | ndk::SpAIBinder | 黏合劑::SpIBinder |
T[] | std::向量<T> | std::向量<T> | 在:&[T] 輸出:Vec<T> |
位元組[] | std::向量<uint8_t> | std::vector<int8_t> 1 | 在:&[u8] 輸出:Vec<u8> |
列表<T> | std::向量<T> 2 | std::向量<T> 3 | 在:&[T] 4 輸出:Vec<T> |
檔案描述符 | android::base::unique_fd | 不適用 | 活頁夾::包裹::包裹文件描述符 |
包裹檔案描述符 | android::os::ParcelFileDescriptor | ndk::作用域檔案描述符 | 活頁夾::包裹::包裹文件描述符 |
介面類型(T) | android::sp<T> | std::shared_ptr<T> | 黏合劑::強 |
可包裹型(T) | 時間 | 時間 | 時間 |
由壬類型 (T) 5 | 時間 | 時間 | 時間 |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; ] |
1. 在 Android 12 或更高版本中,出於相容性原因,位元組數組使用 uint8_t 而不是 int8_t。
2. C++ 後端支援List<T>
,其中T
是String
、 IBinder
、 ParcelFileDescriptor
或 Parcelable 之一。在 Android 13 或更高版本中, T
可以是除數組之外的任何非基本類型(包括介面類型)。 AOSP 建議您使用T[]
等陣列類型,因為它們適用於所有後端。
3. NDK 後端支援List<T>
,其中T
是String
、 ParcelFileDescriptor
或 Parcelable 之一。在 Android 13 或更高版本中, T
可以是除數組之外的任何非基本類型。
4. Rust 程式碼的類型傳遞方式不同,取決於它們是輸入(參數)還是輸出(回傳值)。
5. Android 12及更高版本支援聯合類型。
6.在Android 13或更高版本中,支援固定大小的陣列。固定大小的陣列可以有多個維度(例如int[3][4]
)。在Java後端,固定大小的陣列被表示為陣列類型。
方向性(輸入/輸出/輸入輸出)
指定函數參數的類型時,可以將它們指定為in
、 out
或inout
。這控制 IPC 呼叫的訊息傳遞方向。 in
是預設方向,表示資料從呼叫者傳遞到被呼叫者。 out
表示資料從被呼叫者傳遞到呼叫者。 inout
是這兩者的組合。但是,Android 團隊建議您避免使用參數說明符inout
。如果將inout
與版本化介面和較舊的被呼叫者一起使用,則僅在呼叫者中存在的附加欄位將重設為其預設值。對於 Rust,普通的inout
型別接收&mut Vec<T>
,列表inout
型別接收&mut Vec<T>
。
UTF8/UTF16
使用 CPP 後端,您可以選擇字串是 utf-8 還是 utf-16。在 AIDL 中將字串宣告為@utf8InCpp String
以自動將其轉換為 utf-8。 NDK 和 Rust 後端始終使用 utf-8 字串。有關utf8InCpp
註釋的更多信息,請參閱AIDL 中的註釋。
可空性
您可以使用@nullable
註解Java後端中可以為null的類型,以將null值公開給CPP和NDK後端。在 Rust 後端中,這些@nullable
類型被公開為Option<T>
。本機伺服器預設拒絕空值。唯一的例外是interface
和IBinder
類型,對於 NDK 讀取和 CPP/NDK 寫入,它們始終可以為 null。有關可為nullable
註釋的更多信息,請參閱AIDL 中的註釋。
客製化包裹
自訂 Parcelable是在目標後端手動實現的 Parcelable。只有當您嘗試為無法變更的現有自訂 Parcelable 新增對其他語言的支援時,才使用自訂 Parcelable。
為了聲明自訂 Parcelable 以便 AIDL 了解它,AIDL Parcelable 聲明如下所示:
package my.pack.age;
parcelable Foo;
預設情況下,這宣告了一個 Java Parcelable,其中my.pack.age.Foo
是一個實作Parcelable
介面的 Java 類別。
對於 AIDL 中的自訂 CPP 後端 Parcelable 的聲明,請使用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 可打包聲明,請使用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(AOSP 實驗版)中,若要在 AIDL 中宣告自訂 Rust Parcelable,請使用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 作業系統提供內建錯誤類型供服務在報告錯誤時使用。它們由 Binder 使用,並且可由任何實作 Binder 介面的服務使用。它們的使用在 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
以及使用者定義的錯誤。
內建錯誤類型可以在以下文件中找到:
後端 | 定義 |
---|---|
爪哇 | android/os/Parcel.java |
消費者保護計劃 | binder/Status.h |
NDK | android/binder_status.h |
鏽 | 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 後端包含<my/package/IFoo.h>
(或<aidl/my/package/IFoo.h>
用於 NDK 後端)。
實施服務
若要實現服務,您必須從本機存根類別繼承。此類從活頁夾驅動程式讀取命令並執行您實現的方法。假設您有一個如下所示的 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 平台進行編譯時完成。
在爪哇中:
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::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
中才能離開非同步上下文。
與死亡相連
您可以要求在託管活頁夾的服務終止時收到通知。這有助於避免洩漏回調代理或協助錯誤恢復。對活頁夾代理物件進行這些呼叫。
- 在 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。
在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 後端,您需要兩個執行緒池:binder 和 Tokio。這意味著使用非同步 Rust 的應用程式需要特殊考慮,特別是在使用join_thread_pool
時。有關詳細信息,請參閱註冊服務部分。
保留名稱
C++、Java 和 Rust 保留一些名稱作為關鍵字或特定語言使用。雖然 AIDL 不會根據語言規則強制執行限制,但使用與保留名稱相符的欄位或類型名稱可能會導致 C++ 或 Java 編譯失敗。對於 Rust,欄位或類型使用「原始標識符」語法重新命名,可使用r#
前綴存取。
我們建議您盡可能避免在 AIDL 定義中使用保留名稱,以避免不符合人體工學的綁定或徹底的編譯失敗。
如果您的 AIDL 定義中已經有保留名稱,您可以安全地重新命名字段,同時保持協議相容;您可能需要更新程式碼才能繼續構建,但任何已建置的程式都將繼續互通。
要避免的名稱: * C++ 關鍵字* Java 關鍵字* Rust 關鍵字