Burada özetlenen en iyi uygulamalar, özellikle AIDL bir API tanımlamak veya API yüzeyleriyle etkileşim kurmak için kullanıldığında, AIDL arayüzlerini etkili bir şekilde ve arayüzün esnekliğine dikkat ederek geliştirmek için bir kılavuz görevi görür.
AIDL, uygulamaların arka plan işleminde birbirleriyle veya sistemle arayüz oluşturması gerektiğinde bir API tanımlamak için kullanılabilir. AIDL ile uygulamalarda programlama arayüzleri geliştirme hakkında daha fazla bilgi için Android Arayüz Tanımlama Dili (AIDL) başlıklı makaleyi inceleyin. AIDL'nin uygulamadaki örnekleri için HAL'ler için AIDL ve Kararlı AIDL başlıklı makaleleri inceleyin.
Sürüm oluşturma
Bir AIDL API'sinin geriye dönük uyumlu her anlık görüntüsü bir sürüme karşılık gelir.
Anlık görüntü almak için m <module-name>-freeze-api
komutunu çalıştırın. API'nin bir istemcisi veya sunucusu her yayınlandığında (ör. Mainline treninde) anlık görüntü almanız ve yeni bir sürüm oluşturmanız gerekir. Sistemden tedarikçiye API'leri için bu işlem, yıllık platform revizyonuyla birlikte yapılmalıdır.
İzin verilen değişiklik türleri hakkında daha fazla bilgi ve ayrıntı için Arayüzleri sürümleme başlıklı makaleyi inceleyin.
API tasarım kuralları
Genel
1. Her şeyi belgeleyin
- Her yöntemi semantiği, bağımsız değişkenleri, yerleşik istisnaların kullanımı, hizmete özel istisnalar ve dönüş değeri açısından belgeleyin.
- Her arayüzü semantiği için belgeleyin.
- Numaralandırılmış türlerin ve sabitlerin semantik anlamını belgeleme.
- Uygulayıcı için net olmayan her şeyi belgeleyin.
- Uygun yerlerde örnekler verin.
2. Dış Lastik
Türler için büyük harf içeren camel case, yöntemler, alanlar ve bağımsız değişkenler için ise küçük harf içeren camel case kullanın. Örneğin, paketlenebilir bir tür için MyParcelable
, bağımsız değişken için anArgument
. Kısaltmalar için kısaltmayı bir kelime olarak değerlendirin (NFC
-> Nfc
).
[-Wconst-name] Numaralandırma değerleri ve sabitler ENUM_VALUE
ve CONSTANT_NAME
olmalıdır
Arayüzler
1. Adlandırma
[-Winterface-name] Arayüz adı I
ile başlamalıdır (ör. IFoo
).
2. Kimliğe dayalı "nesneler" içeren büyük arayüzlerden kaçının
Belirli bir API ile ilgili çok sayıda çağrı olduğunda alt arayüzleri tercih edin. Bu durum aşağıdaki avantajları sağlar:
- İstemci veya sunucu kodunun anlaşılmasını kolaylaştırır.
- Nesnelerin yaşam döngüsünü basitleştirir
- Bağlayıcıların sahtesinin yapılamamasından yararlanır.
Önerilmez: Kimliğe dayalı nesneler içeren tek ve büyük bir arayüz
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
}
Önerilen: Ayrı arayüzler
interface IManager {
IFoo getFoo();
}
interface IFoo {
void begin(); // clients in other processes can't guess a binder
void op();
}
3. Tek yönlü yöntemlerle çift yönlü yöntemleri karıştırmayın
[-Wmixed-oneway] Tek yönlü yöntemleri tek yönlü olmayan yöntemlerle karıştırmayın. Bu durum, istemciler ve sunucular için iş parçacığı modelini anlamayı zorlaştırır. Özellikle belirli bir arayüzün istemci kodunu okurken her yöntemin engelleyip engellemeyeceğini aramanız gerekir.
4. Dönüş durum kodlarından kaçının
Tüm AIDL yöntemlerinde örtülü bir durum döndürme kodu olduğundan, yöntemler döndürülen değerler olarak durum kodlarını kullanmamalıdır. ServiceSpecificException
veya EX_SERVICE_SPECIFIC
sayfasına bakın. Bu değerler, bir AIDL arayüzünde sabitler olarak tanımlanır. Daha ayrıntılı bilgiyi AIDL arka uçlarının hata işleme bölümünde bulabilirsiniz.
5. Çıkış parametresi olarak kullanılan diziler zararlı olarak kabul edilir
[-Wout-array] void foo(out String[] ret)
gibi dizi çıkış parametrelerine sahip yöntemler genellikle kötüdür. Çünkü çıkış dizisinin boyutu Java'da istemci tarafından bildirilip ayrılmalıdır. Bu nedenle, dizi çıkışının boyutu sunucu tarafından seçilemez. Bu istenmeyen davranış, Java'da dizilerin çalışma şeklinden (yeniden tahsis edilemezler) kaynaklanır. Bunun yerine String[] foo()
gibi API'leri tercih edin.
6. Giriş/çıkış parametrelerinden kaçının
[-Winout-parameter] Bu durum, in
parametreleri bile out
parametrelerine benzediği için müşterilerin kafasını karıştırabilir.
7. Çıkış ve giriş/çıkış @nullable olmayan dizi dışı parametrelerden kaçının
[-Wout-nullable] Java arka ucu @nullable
ek açıklamasını diğer arka uçlar gibi işlemediğinden out/inout @nullable T
, arka uçlar arasında tutarsız davranışlara yol açabilir. Örneğin, Java olmayan arka uçlar bir out @nullable
parametresini null olarak ayarlayabilir (C++'ta std::nullopt
olarak ayarlanır) ancak Java istemcisi bunu null olarak okuyamaz.
Yapılandırılmış parcelable'lar
1. Ne zaman kullanılır?
Gönderilecek birden fazla veri türü olduğunda yapılandırılmış paketlenebilirler kullanın.
Veya tek bir veri türünüz olduğunda ancak gelecekte bunu genişletmeniz gerekeceğini düşündüğünüzde. Örneğin, String username
kullanmayın. Aşağıdaki gibi genişletilebilir bir paketlenebilir nesne kullanın:
parcelable User {
String username;
}
Böylece gelecekte aşağıdaki şekilde uzatabilirsiniz:
parcelable User {
String username;
int id;
}
2. Varsayılanları açıkça sağlama
[-Wexplicit-default, -Wenum-explicit-default] Alanlar için açık varsayılanlar sağlayın.
Yapılandırılmamış Parcelable'lar
1. Ne zaman kullanılır?
Yapılandırılmamış Parcelable'lar, Java'da @JavaOnlyStableParcelable
ile, NDK arka ucunda ise @NdkOnlyStableParcelable
ile kullanılabilir. Bunlar genellikle yapılandırılamayan eski ve mevcut paketlenebilirlerdir.
Sabitler ve sıralamalar
1. Bit alanları sabit alanlar kullanmalıdır
Bit alanları sabit alanlar kullanmalıdır (örneğin, bir arayüzde const int FOO = 3;
).
2. Numaralandırmalar kapalı kümeler olmalıdır.
Numaralandırmalar kapalı kümeler olmalıdır. Not: Yalnızca arayüz sahibi enum öğeleri ekleyebilir. Tedarikçilerin veya OEM'lerin bu alanları genişletmesi gerekiyorsa alternatif bir mekanizma gerekir. Mümkün olduğunda, tedarikçi işlevselliğinin upstreaming'i tercih edilmelidir. Ancak bazı durumlarda, özel tedarikçi değerlerine izin verilebilir (Bununla birlikte, tedarikçilerin bunu sürüm oluşturmak için bir mekanizmaya sahip olması gerekir. Belki de AIDL'nin kendisi, birbirleriyle çakışmamalı ve bu değerler üçüncü taraf uygulamalarına gösterilmemelidir).
3. "NUM_ELEMENTS" gibi değerlerden kaçının.
Numaralandırılmış türler sürümlendirildiğinden kaç değerin mevcut olduğunu belirten değerlerden kaçınılmalıdır. C++'ta bu durum enum_range<>
ile çözülebilir. Rust için enum_values()
kullanın. Java'da henüz bir çözüm bulunmamaktadır.
Önerilmez: Numaralandırılmış değerler kullanmayın.
@Backing(type="int")
enum FruitType {
APPLE = 0,
BANANA = 1,
MANGO = 2,
NUM_TYPES, // BAD
}
4. Gereksiz önek ve soneklerden kaçının
[-Wredundant-name] Sabitlerde ve numaralandırıcılarda gereksiz veya tekrar eden önek ve soneklerden kaçının.
Önerilmez: Gereksiz bir önek kullanma
enum MyStatus {
STATUS_GOOD,
STATUS_BAD // BAD
}
Önerilen: Enum'u doğrudan adlandırma
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] FileDescriptor
öğesinin bağımsız değişken olarak veya bir AIDL arayüzü yönteminin dönüş değeri olarak kullanılması kesinlikle önerilmez. Özellikle AIDL, Java'da uygulandığında dikkatli bir şekilde ele alınmadığı takdirde dosya tanımlayıcı sızıntısına neden olabilir. Temel olarak, bir FileDescriptor
kabul ederseniz artık kullanılmadığında manuel olarak kapatmanız gerekir.
Yerel arka uçlarda, FileDescriptor
, otomatik olarak kapatılabilen unique_fd
ile eşlendiği için güvendesiniz. Ancak kullanacağınız arka uç dilinden bağımsız olarak, gelecekte arka uç dilini değiştirme özgürlüğünüzü sınırlayacağından FileDescriptor
kullanmamanız önerilir.
Bunun yerine, otomatik olarak kapatılabilen ParcelFileDescriptor
özelliğini kullanın.
Değişken birimler
Değişken birimlerin, belgelere başvurmaya gerek kalmadan iyi tanımlanıp anlaşılabilmesi için ada dahil edildiğinden emin olun.
Örnekler
long duration; // Bad
long durationNsec; // Good
long durationNanos; // Also good
double energy; // Bad
double energyMilliJoules; // Good
int frequency; // Bad
int frequencyHz; // Good
Zaman damgaları referanslarını belirtmelidir
Zaman damgaları (aslında tüm birimler!) birimlerini ve referans noktalarını net bir şekilde belirtmelidir.
Örnekler
/**
* 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;