AIDL 後端

AIDL 後端是存根程式碼產生的目標。使用 AIDL 檔案時,您始終以具有特定執行時間的特定語言使用它們。根據上下文,您應該使用不同的 AIDL 後端。

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>-rustrustlib ,可以對其進行連結。有關更多詳細信息,請參閱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> ,其中TStringIBinderParcelFileDescriptor或 Parcelable 之一。在 Android 13 或更高版本中, T可以是除數組之外的任何非基本類型(包括介面類型)。 AOSP 建議您使用T[]等陣列類型,因為它們適用於所有後端。

3. NDK 後端支援List<T> ,其中TStringParcelFileDescriptor或 Parcelable 之一。在 Android 13 或更高版本中, T可以是除數組之外的任何非基本類型。

4. Rust 程式碼的類型傳遞方式不同,取決於它們是輸入(參數)還是輸出(回傳值)。

5. Android 12及更高版本支援聯合類型。

6.在Android 13或更高版本中,支援固定大小的陣列。固定大小的陣列可以有多個維度(例如int[3][4] )。在Java後端,固定大小的陣列被表示為陣列類型。

方向性(輸入/輸出/輸入輸出)

指定函數參數的類型時,可以將它們指定為inoutinout 。這控制 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> 。本機伺服器預設拒絕空值。唯一的例外是interfaceIBinder類型,對於 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 函數中取得錯誤時,所有inoutout參數以及傳回值(在某些後端中充當out參數)應被視為處於不確定狀態。

使用哪些錯誤值

許多內建錯誤值可以在任何 AIDL 介面中使用,但有些會以特殊方式處理。例如,當描述錯誤條件時,可以使用EX_UNSUPPORTED_OPERATIONEX_ILLEGAL_ARGUMENT ,但不能使用EX_TRANSACTION_FAILED ,因為底層基礎設施會對其進行特殊處理。有關這些內建值的更多信息,請檢查後端特定定義。

如果 AIDL 介面需要內建錯誤類型未涵蓋的其他錯誤值,則它們可能會使用特殊的特定於服務的內建錯誤,該內建錯誤允許包含使用者定義的特定於服務的錯誤值。這些特定於服務的錯誤通常在 AIDL 介面中定義為const intint支援的enum ,並且不會由綁定器進行解析。

在 Java 中,錯誤對應到異常,例如android.os.RemoteException 。對於特定服務的異常,Java 使用android.os.ServiceSpecificException以及使用者定義的錯誤。

Android 中的本機程式碼不使用異常。 CPP 後端使用android::binder::Status 。 NDK 後端使用ndk::ScopedAStatus 。 AIDL 產生的每個方法都會傳回其中之一,表示該方法的狀態。 Rust 後端使用與 NDK 相同的異常代碼值,但在將它們傳遞給使用者之前將它們轉換為本機 Rust 錯誤( StatusCodeExceptionCode )。對於特定於服務的錯誤,傳回的StatusScopedAStatus使用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中定義的嵌套類型BarIFoo是檔案的根類型)時,您必須為 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。當位於綁定器事務上下文之外時,這些函數會傳回目前程序的 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 後端,您需要兩個執行緒池:binder 和 Tokio。這意味著使用非同步 Rust 的應用程式需要特殊考慮,特別是在使用join_thread_pool時。有關詳細信息,請參閱註冊服務部分

保留名稱

C++、Java 和 Rust 保留一些名稱作為關鍵字或特定語言使用。雖然 AIDL 不會根據語言規則強制實施限制,但使用與保留名稱相符的欄位或類型名稱可能會導致 C++ 或 Java 編譯失敗。對於 Rust,欄位或類型使用「原始標識符」語法重新命名,可使用r#前綴存取。

我們建議您盡可能避免在 AIDL 定義中使用保留名稱,以避免不符合人體工學的綁定或徹底的編譯失敗。

如果您的 AIDL 定義中已經有保留名稱,您可以安全地重新命名字段,同時保持協議相容;您可能需要更新程式碼才能繼續構建,但任何已建置的程式都將繼續互通。

要避免的名稱: * C++ 關鍵字* Java 關鍵字* Rust 關鍵字