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 mengetahui informasi selengkapnya tentang mengembangkan antarmuka pemrograman dalam aplikasi dengan AIDL, lihat Android Interface Definition Language (AIDL). Untuk contoh cara kerja AIDL, lihat AIDL untuk HAL dan AIDL Stabil.
Pembuatan versi
Setiap snapshot AIDL yang kompatibel dengan versi lama berkaitan dengan suatu versi.
Untuk mengambil snapshot, jalankan m <module-name>-freeze-api
. Setiap kali klien atau server API dirilis (misalnya, di kereta Utama), Anda perlu mengambil snapshot dan membuat versi baru. Untuk API sistem ke vendor, hal ini
harus terjadi dengan revisi platform tahunan.
Untuk 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.
- Mendokumentasikan setiap antarmuka untuk semantiknya.
- Dokumentasikan makna semantik enum dan konstanta.
- Mendokumentasikan apa pun yang mungkin tidak jelas bagi pelaksana.
- Berikan contoh jika relevan.
2. Casing
Gunakan camel casing atas untuk jenis dan camel casing 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
[-Winterface-name] Nama antarmuka harus diawali dengan I
seperti IFoo
.
2. Hindari antarmuka besar dengan "objek" berbasis ID
Pilih subantarmuka jika ada banyak panggilan terkait API tertentu. Tindakan 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 menggabungkan metode satu arah dengan 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. Khususnya, saat membaca kode klien antarmuka tertentu, Anda harus mencari setiap metode apakah metode tersebut akan diblokir atau tidak.
4. Hindari pengembalian kode status
Metode harus menghindari kode status sebagai nilai yang ditampilkan, karena semua metode AIDL memiliki
kode pengembalian status implisit. Lihat ServiceSpecificException
atau
EX_SERVICE_SPECIFIC
. Berdasarkan konvensi, nilai-nilai ini ditetapkan sebagai konstanta dalam
antarmuka AIDL. Informasi lebih detail ada di
bagian Penanganan error pada backend
AIDL.
5. Array sebagai parameter output yang 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 (alokasi tidak dapat dialokasikan ulang). Sebagai gantinya, pilih API
seperti String[] foo()
.
6. Menghindari parameter inout
[-Winout-parameter] Hal ini dapat membingungkan klien karena parameter in
sekalipun terlihat seperti parameter out
.
7. Menghindari parameter non-array @nullable keluar dan masuk
[-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
keluar ke null (pada 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, saat Anda memiliki satu jenis data, tetapi ingin memperluasnya di masa mendatang. Misalnya, jangan gunakan String username
. Gunakan parcelable yang dapat diperluas, seperti berikut:
parcelable User {
String username;
}
Agar, di masa mendatang, Anda dapat memperluasnya, seperti berikut:
parcelable User {
String username;
int id;
}
2. Berikan default secara eksplisit
[-Weksplisit-default, -Wenum-eksplisit-default] Memberikan default eksplisit untuk kolom.
parcelable tidak terstruktur
1. Kapan digunakan
parcelable tidak terstruktur tersedia di Java dengan
@JavaOnlyStableParcelable
dan di backend NDK dengan
@NdkOnlyStableParcelable
. Biasanya, ini adalah paket lama dan sudah 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, sebaiknya prioritaskan fungsi vendor upstreaming. Namun, dalam beberapa kasus, nilai vendor kustom mungkin diizinkan melalui (walaupun, vendor harus memiliki mekanisme untuk membuat versi ini, mungkin AIDL itu sendiri, nilai tersebut 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 berapa banyak nilai yang ada
harus dihindari. Pada C++, masalah ini dapat diatasi dengan, enum_range<>
. Untuk
Rust, gunakan enum_values()
. Di Java, belum ada solusi.
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 enumerator.
Tidak direkomendasikan: Menggunakan awalan duplikat
enum MyStatus {
STATUS_GOOD,
STATUS_BAD // BAD
}
Direkomendasikan: Menamai enum secara langsung
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] Penggunaan FileDescriptor
sebagai argumen atau nilai
yang ditampilkan metode antarmuka AIDL sangat tidak disarankan. Khususnya, saat
AIDL diimplementasikan di Java, hal ini dapat menyebabkan kebocoran deskriptor file, kecuali
ditangani dengan hati-hati. Pada dasarnya, jika menerima 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 TIDAK menggunakan 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;