Burada açıklanan en iyi uygulamalar, özellikle AIDL bir API'yi 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 API tanımlamak için kullanılabilir. AIDL içeren 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. Uygulamadaki AIDL örnekleri için HAL'ler için AIDL ve Kararlı AIDL'ye bakın.
Sürüm oluşturma
Bir AIDL API'nin geriye dönük uyumlu her anlık görüntüsü bir sürüme karşılık gelir.
Ekran görüntüsü almak için m <module-name>-freeze-api
komutunu çalıştırın. API'nin bir istemcisi veya sunucusu her yayınlandığında (ör. ana hat treninde) anlık görüntü almanız ve yeni bir sürüm oluşturmanız gerekir. Sistemden tedarikçiye API'ler için bu işlem yıllık platform revizyonunda gerçekleşir.
İzin verilen değişiklik türleri hakkında daha fazla bilgi için Arayüz sürümlendirme başlıklı makaleyi inceleyin.
API tasarımı yönergeleri
Genel
1. Her şeyi belgeleyin
- Her yöntemin anlamsal özelliklerini, bağımsız değişkenlerini, yerleşik istisnaların kullanımını, hizmete özgü istisnaları ve döndürülen değeri belgeleyin.
- Her arayüzün anlamsallığını belgeleyin.
- Listelemelerin ve sabitlerin anlamsal anlamını açıklayın.
- Uygulamacı için net olmayan her şeyi belgeleyin.
- Uygun durumlarda örnek verin.
2. Dış lastik
Türler için büyük harfle, yöntemler, alanlar ve bağımsız değişkenler için küçük harfle başlayın. Örneğin, ayrıştırılabilir tür için MyParcelable
, bağımsız değişken için anArgument
. Kısaltmalar için kelime kısaltmasını kullanabilirsiniz (NFC
-> Nfc
).
[-Wconst-name] Enum değerleri ve sabitler ENUM_VALUE
ve CONSTANT_NAME
olmalıdır
Arayüzler
1. Adlandırma
[-Winterface-name] Arayüz adı, IFoo
gibi I
ile başlamalıdır.
2. Kimlik tabanlı "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 dövülemez olmasından faydalanır.
Önerilmez: Kimlik tabanlı nesnelerin bulunduğu 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öntemleri iki yönlü yöntemlerle karıştırmayın
[-Wmixed-oneway] Tek yönlü yöntemlerle tek yönlü olmayan yöntemleri karıştırmayın. Aksi takdirde, istemciler ve sunucular için mesaj dizileri modelini anlama işlemi karmaşık hale gelir. Daha açık belirtmek gerekirse, belirli bir arayüzün istemci kodunu okurken her yöntemin engelleyip engellemeyeceğini kontrol etmeniz gerekir.
4. Durum kodları döndürmekten kaçının
Tüm AIDL yöntemlerinin gizli bir durum döndürme kodu olduğundan, yöntemler dönüş değeri olarak durum kodlarından kaçınmalıdır. ServiceSpecificException
veya
EX_SERVICE_SPECIFIC
başlıklı makaleyi inceleyin. Bu değerler, kural olarak bir AIDL arayüzünde sabit olarak tanımlanır. Daha ayrıntılı bilgi için AIDL arka uçlarının hata işleme bölümüne bakın.
5. Çıkış parametresi olarak kullanılan diziler zararlı olarak kabul edilir
[-Wout-array] void foo(out String[] ret)
gibi dizi çıkış parametreleri olan yöntemler genellikle kötüdür çünkü çıkış dizisi boyutu Java'da istemci tarafından tanımlanmalı ve ayrılmalıdır. Bu nedenle, dizi çıkışının boyutu sunucu tarafından seçilemez. Bu istenmeyen davranış, dizilerin Java'da işleyiş şekli nedeniyle (yeniden ayrılamazlar) ortaya çıkar. Bunun yerine String[] foo()
gibi API'leri tercih edin.
6. Giriş/çıkış parametrelerinden kaçının
[-Winout-parameter] in
parametreleri bile out
parametrelerine benzediği için bu durum müşterilerin kafasını karıştırabilir.
7. out ve inout @nullable olmayan dizi dışı parametrelerden kaçının
[-Wout-nullable] Java arka ucu, diğer arka uçlar @nullable
ek açıklamasını işlemediği için out/inout @nullable T
arka uçlar arasında tutarsız davranışlara neden olabilir. Örneğin, Java olmayan arka uçlar @nullable
parametresini null olarak ayarlayabilir (C++'ta std::nullopt
olarak ayarlayabilir) ancak Java istemcisi bunu null olarak okuyamaz.
Yapılandırılmış paketlenebilir öğeler
1. Ne zaman kullanılır?
Gönderilecek birden fazla veri türünün olduğu yapılandırılmış ayrıştırıcıları kullanın.
Tek bir veri türünü kullanırken gelecekte bu türü genişletmeniz gerekebileceğini düşünüyorsanız da bu yöntemi kullanabilirsiniz. Örneğin, String username
kullanmayın. Aşağıdaki gibi genişletilebilir bir paketlenebilir öğe kullanın:
parcelable User {
String username;
}
Böylece, gelecekte aşağıdaki gibi genişletebilirsiniz:
parcelable User {
String username;
int id;
}
2. Varsayılanları açıkça belirtin
[-Wexplicit-default, -Wenum-explicit-default] Alanlar için açık varsayılanlar sağlayın.
Yapılandırılmamış paketlenebilir öğeler
1. Ne zaman kullanılır?
Yapılandırılmamış paketlenebilirler, Java'da @JavaOnlyStableParcelable
ile ve NDK arka ucunda @NdkOnlyStableParcelable
ile kullanılabilir. Bunlar genellikle yapılandırılamayan eski ve mevcut paketlenebilir öğelerdir.
Sabitler ve sıralamalar
1. Bit alanları sabit alanlar kullanmalıdır
Bit alanları sabit alanlar (ör. bir arayüzde const int FOO = 3;
) kullanmalıdır.
2. Listeler kapalı kümeler olmalıdır.
Listeler kapalı kümeler olmalıdır. Not: Yalnızca arayüz sahibi, enum öğeleri ekleyebilir. Tedarikçi firmaların veya OEM'lerin bu alanları genişletmesi gerekiyorsa alternatif bir mekanizmaya ihtiyaç vardır. Mümkün olduğunda tedarikçi işlevini yayına aktarma tercih edilmelidir. Ancak bazı durumlarda özel tedarikçi değerlerine izin verilebilir (ancak tedarikçilerin bunu sürümlendirecek bir mekanizması olmalıdır, belki de AIDL'nin kendisi, birbirleriyle çakışamaz ve bu değerler üçüncü taraf uygulamalarına gösterilmemelidir).
3. "NUM_ELEMENTS" gibi değerlerden kaçının
Listeler sürüm içerdiğinden, kaç tane değer olduğunu belirten değerlerden kaçınılmalıdır. C++'ta bu sorun enum_range<>
ile çözülebilir. Rust için enum_values()
kullanın. Java'da henüz bir çözüm bulunmuyor.
Önerilmez: Numaralı değerler kullanma
@Backing(type="int")
enum FruitType {
APPLE = 0,
BANANA = 1,
MANGO = 2,
NUM_TYPES, // BAD
}
4. Gereksiz ön eklerden ve son eklerden kaçının
[-Wredundant-name] Sabitler ve sayılandırıcılarda gereksiz veya yinelenen ön eklerden ve son eklerden kaçının.
Önerilmez: Gereksiz bir önek kullanılması
enum MyStatus {
STATUS_GOOD,
STATUS_BAD // BAD
}
Önerilen: Sıralamayı doğrudan adlandırma
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] FileDescriptor
değerinin bir bağımsız değişken veya AIDL arayüz yönteminin döndürdüğü değer olarak kullanılması önerilmez. Bu durum, özellikle AIDL Java'da uygulandığında, dikkatli bir şekilde ele alınmadığı takdirde dosya tanımlayıcısı sızıntısına neden olabilir. Özetle, bir FileDescriptor
'yi kabul ederseniz artık kullanılmadığında manuel olarak kapatmanız gerekir.
Doğal arka uçlarda FileDescriptor
, otomatik olarak kapatılabilen unique_fd
ile eşlendiğinden güvendesiniz. Ancak kullanacağınız arka uç dilinden bağımsız olarak, FileDescriptor
kullanmamak akıllıca bir seçimdir. Aksi takdirde, gelecekte arka uç dilini değiştirme özgürlüğünüz sınırlanır.
Bunun yerine, otomatik olarak kapatılabilen ParcelFileDescriptor
seçeneğini kullanın.
Değişken birimler
Değişken birimlerinin ada eklendiğinden emin olun. Böylece, birimlerin belgelere referansta bulunmaya gerek kalmadan iyi tanımlanmasını ve anlaşılmasını sağlayabilirsiniz.
Ö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ında referans belirtilmelidir.
Zaman damgaları, birimlerini ve referans noktalarını açıkça 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;