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 saat aplikasi perlu berinteraksi satu sama lain dalam proses latar belakang atau perlu berinteraksi dengan sistem. Untuk mengetahui 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 API AIDL yang kompatibel dengan versi sebelumnya sesuai dengan sebuah versi.
Untuk mengambil snapshot, jalankan m <module-name>-freeze-api
. Setiap kali klien atau server API dirilis (misalnya, dalam rilis Mainline), Anda perlu mengambil snapshot dan membuat versi baru. Untuk API sistem ke vendor, hal ini harus
terjadi dengan revisi platform tahunan.
Untuk mengetahui detail dan informasi selengkapnya tentang jenis perubahan yang diizinkan, lihat Membuat versi antarmuka.
Pedoman 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 huruf kapital di awal setiap kata (upper camel case) untuk jenis dan huruf kapital di awal setiap kata (lower camel case) untuk metode, kolom, dan argumen. Misalnya, MyParcelable
untuk jenis parcelable dan anArgument
untuk argumen. Untuk akronim, anggap 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
Lebih memilih subantarmuka jika ada banyak panggilan yang terkait dengan API tertentu. Hal ini memberikan manfaat sebagai berikut:
- Membuat kode klien atau server lebih mudah dipahami
- Membuat siklus proses objek menjadi lebih sederhana
- Memanfaatkan pengikat yang tidak dapat dipalsukan.
Tidak direkomendasikan: Satu antarmuka 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 dua arah
[-Wmixed-oneway] Jangan mencampur metode satu arah dengan metode non-satu arah, karena akan membuat pemahaman model threading menjadi rumit bagi klien dan server. Secara khusus, saat membaca kode klien dari antarmuka tertentu, Anda perlu mencari tahu apakah setiap metode akan memblokir atau tidak.
4. Menghindari kode status yang ditampilkan
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
. Menurut konvensi, nilai ini ditentukan sebagai konstanta dalam
antarmuka AIDL. Informasi yang lebih mendetail 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 tidak baik 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 (tidak dapat dialokasikan ulang). Sebagai gantinya, gunakan API seperti String[] foo()
.
6. Hindari parameter inout
[-Winout-parameter] Hal ini dapat membingungkan klien karena bahkan parameter in
terlihat
seperti parameter out
.
7. Hindari parameter non-array @nullable out dan inout
[-Wout-nullable] Karena backend Java tidak menangani anotasi @nullable
, sedangkan backend lain menanganinya, out/inout @nullable T
dapat menyebabkan perilaku yang tidak konsisten di seluruh backend. Misalnya, backend non-Java dapat menyetel parameter @nullable
out ke null (di C++, menyetelnya sebagai std::nullopt
), tetapi klien Java tidak dapat membacanya sebagai null.
Parcel terstruktur
1. Kapan digunakan
Gunakan parcel terstruktur jika Anda memiliki beberapa jenis data yang akan dikirim.
Atau, saat Anda memiliki satu jenis data, tetapi Anda memperkirakan bahwa Anda akan perlu memperluasnya pada masa mendatang. Misalnya, jangan gunakan String username
. Gunakan parcelable yang dapat diperluas, seperti berikut:
parcelable User {
String username;
}
Sehingga, di masa mendatang, Anda dapat memperluasnya, sebagai berikut:
parcelable User {
String username;
int id;
}
2. Menyediakan default secara eksplisit
[-Wexplicit-default, -Wenum-explicit-default] Berikan 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 parcelable lama dan yang sudah ada yang tidak dapat distrukturkan.
Konstanta dan enum
1. Bitfield harus menggunakan kolom konstanta
Bitfield harus menggunakan kolom konstanta (misalnya, const int FOO = 3;
dalam
antarmuka).
2. Enum harus berupa set tertutup.
Enum harus berupa set tertutup. Catatan: hanya pemilik antarmuka yang dapat menambahkan elemen enum. Jika vendor atau OEM perlu memperluas kolom ini, mekanisme alternatif diperlukan. Jika memungkinkan, sebaiknya gunakan fungsi vendor upstream. Namun, dalam beberapa kasus, nilai vendor kustom mungkin diizinkan melalui (meskipun, 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 diberi 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 enumerator.
Tidak direkomendasikan: Menggunakan awalan yang berlebihan
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
kembalian metode antarmuka AIDL sangat tidak disarankan. Terutama, saat
AIDL diimplementasikan di Java, hal ini dapat menyebabkan kebocoran deskriptor file kecuali jika ditangani dengan hati-hati. Pada dasarnya, jika Anda menerima FileDescriptor
, Anda harus menutupnya secara manual saat tidak lagi digunakan.
Untuk backend native, Anda aman karena FileDescriptor
dipetakan ke unique_fd
yang dapat ditutup otomatis. Namun, terlepas dari bahasa backend yang akan Anda gunakan, sebaiknya JANGAN 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 satuan variabel disertakan dalam nama sehingga satuannya ditentukan dan dipahami dengan baik tanpa perlu merujuk ke 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 (bahkan 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;