AIDL 後端

AIDL 後端是產生虛設常式程式碼的目標。使用 AIDL 檔案時 一律以特定語言使用這些函式,並搭配特定執行階段。視乎 因此,建議您使用不同的 AIDL 後端。

下表的 API 介面穩定性是指 針對這個 API 介面編譯程式碼 獨立於 system.img libbinder.so 二進位檔中傳送。

AIDL 有下列後端:

後端 語言 API 介面 建構系統
Java Java SDK/SystemApi (穩定版*) 全部
NDK C++ libbinder_ndk (穩定版*) aidl_介面
CPP C++ libbinder (不穩定) 全部
荒漠油廠 荒漠油廠 libbinder_rs (穩定*) aidl_介面
  • 這些 API 介面相當穩定,但許多 API (例如服務用的 API) 管理,僅供內部平台使用,無法 應用程式。如要進一步瞭解如何在應用程式中使用 AIDL,請參閱 開發人員說明文件
  • Rust 後端是在 Android 12 中導入。這個 自 Android 10 起,NDK 後端已可使用。
  • Rust Crate 是以 libbinder_ndk 為基礎建構而成, 確保穩定可靠APEX 使用繫結器 Crate 的方式與任何人相同 系統端的另一個問題Rust 部分包含在 APEX 中並隨附於 APEX 建立 Deployment依附於系統分區的 libbinder_ndk.so

建構系統

視後端而定,有兩種方法可將 AIDL 編譯為虛設常式 再也不是件繁重乏味的工作如要進一步瞭解建構系統,請參閱 Soong 模組參考資料

核心建構系統

在任何 cc_java_ Android.bp 模組中 (或在 Android.mk 的對等項目中), 您可以將 .aidl 檔案指定為來源檔案,在本例中,Java/CPP 的 系統會使用 AIDL 後端 (而非 NDK 後端) 的類別,以及要使用 對應的 AIDL 檔案會自動新增至模組。選項 local_include_dirs 之類的要求,用於向建構系統告知 該模組中的 AIDL 檔案可在這些模組中的 aidl: 底下指定 群組。請注意,Rust 後端只能與 Rust 搭配使用。rust_ 個模組 會以不同方式處理,但不會將 AIDL 檔案指定為來源檔案。 反之,aidl_interface 模組會產生名為 rustlibrustlib <aidl_interface name>-rust 可以建立連結。詳情請參閱 Rust AIDL 範例

aidl_介面

搭配這個建構系統使用的類型必須結構化。為了清楚規劃 parcelables 必須包含直接包含欄位,且不得為類型宣告 直接使用目標語言定義結構化 AIDL 如何與 穩定的 AIDL,請參閱結構化與穩定的 AIDL

類型

您可以將 aidl 編譯器視為類型的參照實作。 建立介面時,叫用 aidl --lang=<backend> ... 就能查看 產生的介面檔案。使用 aidl_interface 模組時,您可以檢視 out/soong/.intermediates/<path to module>/ 中的輸出內容

Java/AIDL 類型 C++ 類型 NDK 類型 Rust 類型
布林值 布林值 布林值 布林值
byte 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 T T
聯集類型 (T)5 T T T
T[N] 6 std::陣列<T, N> std::陣列<T, N> [T;北

1. 在 Android 12 以上版本中,位元組陣列會使用 基於相容性因素而採用 uint8_t 而非 int8_t。 ,瞭解如何調查及移除這項存取權。

2. C++ 後端支援 List<T>,其中 TString 之一, IBinderParcelFileDescriptor 或 parcelable。在 Android 中 13 以上,T 可以是任何非原始類型 (包括介面類型) 但陣列除外。Android 開放原始碼計畫建議你 使用 T[] 之類的陣列類型,因為它們適用於所有後端。

3. NDK 後端支援 List<T>,其中 TString 之一。 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\> 個物件 如果繫結器為其他物件,就會由內部管理 上傳資料集之後,您可以運用 AutoML 自動完成部分資料準備工作透過其他方式建立物件會導致重複擁有權。 ,瞭解如何調查及移除這項存取權。

方向 (包含/傳出/傳出)

將引數類型指定至函式時,您可以指定 格式為 inoutinout。這會控制路線資訊的 。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。宣告 字串為 @utf8InCpp String,自動將其轉換為 utf-8。 NDK 和 Rust 後端一律使用 utf-8 字串。如要進一步瞭解 utf8InCpp 註解,請參閱 AIDL 中的註解

是否可為空值

您可以使用 @nullable 為 Java 後端中可為空值的類型加上註解 向 CPP 和 NDK 後端公開空值。在 Rust 後端 @nullable 類型會以 Option<T> 公開。原生伺服器拒絕空值 根據預設。唯一的例外狀況是 interfaceIBinder 類型 在 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 是 Java 實作 Parcelable 介面的類別。

如要在 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 開放原始碼計畫實驗功能) 中,對自訂 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 檔案的類型, 產生的符記為 CPP/NDK 後端提供 <== 運算子 在 union 中使用自訂 parcelable

預設值

結構化 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 取得錯誤 函式、所有 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 以及 使用者定義的錯誤。

內建錯誤類型位於下列檔案:

後端 定義
Java android/os/Parcel.java
CPP binder/Status.h
NDK android/binder_status.h
荒漠油廠 android/binder_status.h

使用各種後端

以下操作說明僅適用於 Android 平台程式碼。這些範例使用 已定義的類型「my.package.IFoo」如需 Rust 後端使用方式的操作說明 請參考 Rust AIDL 範例 Android Rust 模式 頁面。

匯入類型

無論已定義的類型是介面、parcelable 或 union,您都可以匯入 在 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>)。

導入服務

如要實作服務,您必須繼承原生虛設常式類別。本課程 讀取繫結器驅動程式中的指令,並執行 假設您有一個 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 註冊 上傳資料集之後,您可以運用 AutoML 自動完成部分資料準備工作除了下列 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 擁有回呼,您必須讓物件持續留存, 做為接收通知的選擇

來電者資訊

收到核心繫結器呼叫時,系統會在以下位置顯示來電者資訊: 以及數個 APIPID (或程序 ID) 是指 傳送交易的流程UID (或 User-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 取得 並在偵錯時額外提供服務

如要為服務新增自訂輸出內容,您可以覆寫 dump 方法,就跟您實作其他任何處理序間通訊 (IPC) 方法一樣 定義於 AIDL 檔案執行此操作時,請限制應用程式的轉儲功能 權限 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 定義中已有保留名稱,您可以放心 重新命名欄位,但仍與通訊協定相容。您可能需要更新 繼續建構,不過已建構的程式會繼續 可以互通。

應避免的名稱: