Panduan Gaya AIDL

Praktik terbaik yang diuraikan di sini berfungsi sebagai panduan untuk mengembangkan antarmuka AIDL secara efektif dan dengan memperhatikan fleksibilitas antarmuka, terutama ketika AIDL digunakan untuk mendefinisikan API atau berinteraksi dengan permukaan API.

AIDL dapat digunakan untuk mendefinisikan API ketika aplikasi perlu berinteraksi satu sama lain dalam proses latar belakang atau perlu berinteraksi dengan sistem. Untuk informasi selengkapnya tentang mengembangkan antarmuka pemrograman dalam aplikasi dengan AIDL, lihat Android Interface Definition Language (AIDL) . Untuk contoh AIDL dalam praktiknya, lihat AIDL untuk HAL dan AIDL Stabil .

Pembuatan versi

Setiap snapshot API AIDL yang kompatibel dengan versi sebelumnya. Untuk mengambil snapshot, jalankan m <module-name>-freeze-api . Setiap kali klien atau server API dirilis (misalnya di kereta jalur utama), Anda perlu mengambil snapshot dan membuat versi baru. Untuk API sistem-ke-vendor, hal ini harus dilakukan dengan revisi platform tahunan.

Untuk detail dan informasi selengkapnya tentang jenis perubahan yang diperbolehkan, lihat Antarmuka pembuatan versi .

Pedoman desain API

Umum

1. Dokumentasikan semuanya

  • Dokumentasikan setiap metode untuk semantik, argumen, penggunaan pengecualian bawaan, pengecualian khusus layanan, dan nilai kembalian.
  • Dokumentasikan setiap antarmuka untuk semantiknya.
  • Dokumentasikan makna semantik enum dan konstanta.
  • Dokumentasikan apa pun yang mungkin tidak jelas bagi pelaksana.
  • Berikan contoh jika relevan.

2. Selongsong

Gunakan huruf besar unta untuk tipe dan huruf besar unta bawah untuk metode, bidang, dan argumen. Misalnya, MyParcelable untuk tipe parsel dan anArgument untuk argumen. Untuk akronim, pertimbangkan akronim sebuah kata ( NFC -> Nfc ).

[-Wconst-name] Nilai dan konstanta enum harus ENUM_VALUE dan CONSTANT_NAME

Antarmuka

1. Penamaan

[-Winterface-name] Nama antarmuka harus dimulai dengan I like IFoo .

2. Hindari antarmuka besar dengan "objek" berbasis id

Lebih memilih sub-antarmuka ketika ada banyak panggilan yang terkait dengan API tertentu. Hal ini memberikan keuntungan sebagai berikut: - Membuat kode klien/server lebih mudah dipahami - Membuat siklus hidup objek menjadi lebih sederhana - Memanfaatkan pengikat yang tidak dapat diubah.

Tidak disarankan: Antarmuka tunggal dan besar dengan objek berbasis id

interface IManager {
   int getFooId();
   void beginFoo(int id); // clients in other processes can guess an ID
   void opFoo(int id);
   void recycleFoo(int id); // ownership not handled by type
}

Direkomendasikan: Sub-antarmuka individual

interface IManager {
    IFoo getFoo();
}

interface IFoo {
    void begin(); // clients in other processes can't guess a binder
    void op();
}

3. Jangan mencampuradukkan metode satu arah dengan dua arah

[-Wmixed-oneway] Jangan mencampurkan metode oneway dengan metode non-oneway, karena akan membuat pemahaman model threading menjadi rumit bagi klien dan server. Khususnya, saat membaca kode klien dari antarmuka tertentu, Anda perlu mencari setiap metode apakah metode tersebut akan memblokir atau tidak.

4. Hindari mengembalikan kode status

Metode harus menghindari kode status sebagai nilai kembalian, karena semua metode AIDL memiliki kode pengembalian status implisit. Lihat ServiceSpecificException atau EX_SERVICE_SPECIFIC . Berdasarkan konvensi, nilai-nilai ini didefinisikan sebagai konstanta dalam antarmuka AIDL. Informasi lebih detail ada di bagian Penanganan kesalahan pada AIDL Backends .

5. Array sebagai parameter keluaran dianggap berbahaya

[-Wout-array] Metode yang memiliki parameter keluaran larik, seperti void foo(out String[] ret) biasanya buruk karena ukuran larik keluaran harus dideklarasikan dan dialokasikan oleh klien di Java, sehingga ukuran keluaran larik tidak bisa dipilih oleh server. Perilaku yang tidak diinginkan ini terjadi karena cara kerja array di Java (tidak dapat dialokasikan kembali). Daripada memilih API seperti String[] foo() .

6. Hindari parameter masuk

[-Winout-parameter] Ini dapat membingungkan klien karena parameter in pun terlihat seperti parameter out .

7. Hindari parameter non-array keluar/masuk @nullable

[-Wout-nullable] Karena backend Java tidak menangani anotasi @nullable sementara backend lain menanganinya, out/inout @nullable T dapat menyebabkan perilaku tidak konsisten di seluruh backend. Misalnya, backend non-Java dapat menyetel parameter out @nullable ke null (di C++, menyetelnya sebagai std::nullopt ) tetapi klien Java tidak dapat membacanya sebagai null.

Paket terstruktur

1. Kapan digunakan

Gunakan paket terstruktur di mana Anda memiliki beberapa tipe data untuk dikirim.

Atau, saat Anda saat ini memiliki satu tipe data tetapi Anda berharap perlu memperluasnya di masa mendatang. Misalnya, jangan gunakan String username . Gunakan parsel yang dapat diperpanjang, seperti berikut:

parcelable User {
    String username;
}

Sehingga kedepannya dapat diperpanjang sebagai berikut:

parcelable User {
    String username;
    int id;
}

2. Berikan default secara eksplisit

[-Wexplicit-default, -Wenum-explicit-default] Memberikan default eksplisit untuk bidang.

Parcelable tidak terstruktur

1. Kapan digunakan

Parcelable tidak terstruktur saat ini tersedia di Java dengan @JavaOnlyStableParcelable dan di backend NDK dengan @NdkOnlyStableParcelable . Biasanya, ini adalah parsel lama dan sudah ada yang tidak dapat disusun dengan mudah.

Konstanta dan Enum

1. Bitfield harus menggunakan kolom konstan

Bitfields harus menggunakan bidang konstan (misalnya const int FOO = 3; dalam antarmuka).

2. Enum harus berupa himpunan tertutup.

Enum harus berupa set tertutup. Catatan: hanya pemilik antarmuka yang dapat menambahkan elemen enum. Jika vendor atau OEM perlu memperluas bidang ini, diperlukan mekanisme alternatif. Jika memungkinkan, fungsionalitas vendor hulu harus diutamakan. Namun, dalam beberapa kasus, nilai vendor khusus mungkin diizinkan (walaupun, vendor harus memiliki mekanisme untuk membuat versi ini, mungkin AIDL itu sendiri, nilai tersebut tidak boleh bertentangan satu sama lain, dan nilai ini tidak boleh bertentangan terkena aplikasi pihak ketiga).

3. Hindari nilai seperti "NUM_ELEMENTS"

Karena enum memiliki versi, nilai yang menunjukkan berapa banyak nilai yang ada harus dihindari. Di C++, ini bisa diatasi dengan, enum_range<> . Untuk Rust, gunakan enum_values() . Di Jawa, belum ada solusi.

Tidak disarankan: Menggunakan nilai bernomor

@Backing(type="int")
enum FruitType {
    APPLE = 0,
    BANANA = 1,
    MANGO = 2,
    NUM_TYPES, // BAD
}

4. Hindari prefiks dan sufiks yang berlebihan

[-Wredundant-name] Hindari awalan dan sufiks yang berlebihan atau berulang dalam konstanta dan enumerator.

Tidak disarankan: Menggunakan awalan yang berlebihan

enum MyStatus {
    STATUS_GOOD,
    STATUS_BAD // BAD
}

Direkomendasikan: Memberi nama enum secara langsung

enum MyStatus {
    GOOD,
    BAD
}

Deskripsi File

[-Wfile-descriptor] Penggunaan FileDescriptor sebagai argumen atau nilai kembalian metode antarmuka AIDL sangat tidak disarankan. Terutama, ketika AIDL diimplementasikan di Java, hal ini mungkin menyebabkan kebocoran deskriptor file kecuali ditangani dengan hati-hati. Pada dasarnya, jika Anda menerima FileDescriptor , Anda harus menutupnya secara manual ketika tidak lagi digunakan.

Untuk backend asli, Anda aman karena FileDescriptor dipetakan ke unique_fd yang dapat ditutup secara otomatis. Namun apa pun bahasa backend yang akan Anda gunakan, sebaiknya JANGAN gunakan FileDescriptor sama sekali karena ini akan membatasi kebebasan Anda untuk mengubah bahasa backend di masa mendatang.

Sebagai gantinya, gunakan ParcelFileDescriptor , yang dapat ditutup secara otomatis.

Unit variabel

Pastikan unit variabel disertakan dalam nama sehingga unitnya terdefinisi dengan baik dan dipahami tanpa memerlukan dokumentasi referensi

Contoh

long duration; // Bad
long durationNsec; // Good
long durationNanos; // Also good

double energy; // Bad
double energyMilliJoules; // Good

int frequency; // Bad
int frequencyHz; // Good

Stempel waktu harus menunjukkan referensinya

Stempel waktu (pada kenyataannya, semua unit!) harus dengan jelas menunjukkan unit dan titik referensinya.

Contoh

/**
 * Time since device boot in milliseconds
 */
long timestampMs;

/**
 * UTC time received from the NTP server in units of milliseconds
 * since January 1, 1970
 */
long utcTimeMs;