Panduan gaya AIDL

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

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

Pembuatan versi

Setiap snapshot AIDL yang kompatibel dengan versi lama berkaitan dengan sebuah versi. Untuk mengambil snapshot, jalankan m <module-name>-freeze-api. Setiap kali klien atau server API dirilis (misalnya, dalam train Mainline), Anda perlu mengambil snapshot dan membuat versi baru. Untuk API sistem ke vendor, hal ini seharusnya terjadi dengan revisi platform tahunan.

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

Panduan desain API

Umum

1. Dokumentasikan semuanya

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

2. Casing

Gunakan casing camel atas untuk jenis dan casing camel bawah untuk metode, kolom, dan argumen. Misalnya, MyParcelable untuk jenis parcelable dan anArgument untuk argumen. Untuk akronim, pertimbangkan akronim sebagai kata (NFC -> Nfc).

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

Antarmuka

1. Penamaan

[-Wnama-antarmuka] Nama antarmuka harus diawali dengan I seperti IFoo.

2. Hindari antarmuka besar dengan "objek" berbasis ID

Pilih subantarmuka jika ada banyak panggilan yang terkait dengan API tertentu. Hal ini memberikan manfaat berikut:

  • Membuat kode klien atau server lebih mudah dipahami
  • Menyederhanakan siklus proses objek
  • Memanfaatkan binder yang tidak dapat dipalsukan.

Tidak direkomendasikan: 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: Antarmuka individual

interface IManager {
    IFoo getFoo();
}

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

3. Jangan mencampur metode satu arah dengan metode dua arah

[-Wmixed-oneway] Jangan mencampur metode satu arah dengan metode tidak satu arah, karena hal ini akan membuat pemahaman model threading menjadi rumit bagi klien dan server. Secara khusus, saat membaca kode klien dari antarmuka tertentu, Anda perlu mencari setiap metode apakah metode tersebut akan memblokir atau tidak.

4. Menghindari menampilkan kode status

Metode harus menghindari kode status sebagai nilai yang ditampilkan, karena semua metode AIDL memiliki kode status yang ditampilkan secara implisit. Lihat ServiceSpecificException atau EX_SERVICE_SPECIFIC. Sesuai dengan konvensi, nilai ini ditentukan sebagai konstanta di antarmuka AIDL. Informasi lebih detail ada di bagian Penanganan error pada backend AIDL.

5. Array sebagai parameter output dianggap berbahaya

[-Wout-array] Metode yang memiliki parameter output array, seperti void foo(out String[] ret) biasanya buruk karena ukuran array output harus dideklarasikan dan dialokasikan oleh klien di Java, sehingga ukuran output array tidak dapat dipilih oleh server. Perilaku yang tidak diinginkan ini terjadi karena cara kerja array di Java (array tidak dapat dialokasikan ulang). Sebagai gantinya, pilih API seperti String[] foo().

6. Menghindari parameter input

[-Winout-parameter] Hal ini dapat membingungkan klien karena parameter in juga terlihat seperti parameter out.

7. Menghindari parameter non-array @nullable out dan inout

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

Parcelable terstruktur

1. Kapan digunakan

Gunakan parcelable terstruktur tempat Anda memiliki beberapa jenis data untuk dikirim.

Atau, jika Anda memiliki satu jenis data, tetapi Anda memperkirakan bahwa Anda perlu memperluasnya di masa mendatang. Misalnya, jangan gunakan String username. Gunakan parcelable yang dapat diperluas, seperti berikut:

parcelable User {
    String username;
}

Sehingga, di masa mendatang, Anda dapat memperpanjangnya, sebagai berikut:

parcelable User {
    String username;
    int id;
}

2. Memberikan default secara eksplisit

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

parcelable tidak terstruktur

1. Kapan digunakan

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

Konstanta dan enum

1. Bitfield harus menggunakan kolom konstan

Bitfield harus menggunakan kolom konstan (misalnya, const int FOO = 3; dalam antarmuka).

2. Enum harus berupa kumpulan tertutup.

Enum harus berupa kumpulan tertutup. Catatan: hanya pemilik antarmuka yang dapat menambahkan elemen enum. Jika vendor atau OEM perlu memperluas kolom ini, mekanisme alternatif diperlukan. Jika memungkinkan, fungsi vendor upstreaming harus lebih disukai. Namun, dalam beberapa kasus, nilai vendor kustom mungkin diizinkan untuk dilalui (meskipun vendor harus memiliki mekanisme untuk membuat versi ini, mungkin AIDL itu sendiri, mereka tidak boleh saling bertentangan, dan nilai ini tidak boleh diekspos ke aplikasi pihak ketiga).

3. Hindari nilai seperti "NUM_ELEMENTS"

Karena enum memiliki versi, nilai yang menunjukkan jumlah nilai yang ada harus dihindari. Di C++, hal ini dapat diatasi dengan enum_range<>. Untuk Rust, gunakan enum_values(). Di Java, belum ada solusinya.

Tidak direkomendasikan: Menggunakan nilai bernomor

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

4. Hindari awalan dan akhiran yang berlebihan

[-Wredundant-name] Hindari awalan dan akhiran yang berlebihan atau berulang dalam konstanta dan penghitung.

Tidak direkomendasikan: Menggunakan awalan duplikat

enum MyStatus {
    STATUS_GOOD,
    STATUS_BAD // BAD
}

Direkomendasikan: Memberi nama enum secara langsung

enum MyStatus {
    GOOD,
    BAD
}

FileDescriptor

[-Wfile-descriptor] Penggunaan FileDescriptor sebagai argumen atau nilai kembali metode antarmuka AIDL sangat tidak dianjurkan. Terutama, saat AIDL diterapkan di Java, hal ini dapat menyebabkan kebocoran deskriptor file kecuali ditangani dengan cermat. Pada dasarnya, jika Anda menyetujui FileDescriptor, Anda harus menutupnya secara manual saat tidak lagi digunakan.

Untuk backend native, Anda tidak perlu khawatir karena FileDescriptor dipetakan ke unique_fd yang dapat ditutup secara otomatis. Namun, terlepas dari bahasa backend yang akan Anda gunakan, sebaiknya JANGAN gunakan FileDescriptor sama sekali karena hal 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 ditentukan dan dipahami dengan baik tanpa perlu mereferensikan dokumentasi

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 (faktanya, semua unit) harus menunjukkan unit dan titik referensinya dengan jelas.

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;