Bu sayfa, API Konseyi'nin API incelemelerinde uyguladığı genel ilkeleri geliştiricilerin anlayabilmesi için bir rehber olarak tasarlanmıştır.
Geliştiriciler, API yazarken bu yönergelere uymanın yanı sıra API Lint aracını çalıştırmalıdır. Bu araç, bu kuralların çoğunu API'lere karşı yaptığı kontrollere kodlar.
Bu dokümanı, söz konusu Lint aracının uyduğu kurallara ilişkin bir kılavuz ve bu araçta yüksek doğrulukla kodlanamayan kurallarla ilgili genel tavsiyeler olarak düşünebilirsiniz.
API Lint aracı
API Lint, Metalava statik analiz aracına entegre edilmiştir ve CI'de doğrulama sırasında otomatik olarak çalışır. m
checkapi
kullanarak yerel bir platform ödemesinden veya ./gradlew :path:to:project:checkApi
kullanarak yerel bir AndroidX ödemesinden manuel olarak çalıştırabilirsiniz.
API kuralları
Android platformu ve birçok Jetpack kitaplığı bu yönergeler oluşturulmadan önce mevcuttu. Bu sayfada daha sonra belirtilen politikalar, Android ekosisteminin ihtiyaçlarını karşılamak için sürekli olarak gelişmektedir.
Bu nedenle, mevcut bazı API'ler kurallara uymayabilir. Diğer durumlarda, yeni bir API'nin yönergelere tam olarak uymak yerine mevcut API'lerle tutarlı olması, uygulama geliştiriciler için daha iyi bir kullanıcı deneyimi sağlayabilir.
Bir API ile ilgili çözülmesi gereken zor sorular veya güncellenmesi gereken yönergeler varsa API Konseyi ile iletişime geçin.
API temel bilgileri
Bu kategori, Android API'sinin temel yönleriyle ilgilidir.
Tüm API'ler uygulanmalıdır
Bir API'nin kitlesinden (ör. herkese açık veya @SystemApi
) bağımsız olarak, tüm API yüzeyleri birleştirilirken veya API olarak gösterilirken uygulanmalıdır. API taslaklarını daha sonra yapılacak uygulamayla birleştirmeyin.
Uygulaması olmayan API yüzeylerinde birden fazla sorun vardır:
- Uygun veya eksiksiz bir yüzeyin açığa çıkarıldığı garanti edilmez. Bir API istemciler tarafından test edilip kullanılana kadar, istemcinin özelliği kullanabilmek için uygun API'lere sahip olduğunu doğrulamanın bir yolu yoktur.
- Uygulaması olmayan API'ler Developer Preview'lerde test edilemez.
- Uygulaması olmayan API'ler CTS'de test edilemez.
Tüm API'ler test edilmelidir
Bu, platform CTS şartlarına, AndroidX politikalarına ve genel olarak API'lerin uygulanması gerektiği fikrine uygundur.
API yüzeylerini test etmek, API yüzeyinin kullanılabilir olduğu ve beklenen kullanım alanlarını ele aldığımız konusunda temel bir garanti sağlar. Varlığını test etmek yeterli değildir; API'nin davranışının da test edilmesi gerekir.
Yeni bir API ekleyen bir değişiklik, aynı CL veya Gerrit konusunda ilgili testleri içermelidir.
API'ler test edilebilir olmalıdır. "Bir uygulama geliştirici, API'nizi kullanan kodu nasıl test eder?" sorusunu yanıtlayabilmeniz gerekir.
Tüm API'ler belgelenmelidir.
Belgeler, API kullanılabilirliğinin önemli bir parçasıdır. Bir API yüzeyinin söz dizimi açıkça anlaşılabilir olsa da yeni istemciler API'nin arkasındaki anlamları, davranışları veya bağlamı anlamaz.
Oluşturulan tüm API'ler yönergelere uygun olmalıdır.
Araçlar tarafından oluşturulan API'ler, elle yazılmış kodla aynı API yönergelerine uymalıdır.
API oluşturmak için önerilmez araçlar:
AutoValue
: Yönergeleri çeşitli şekillerde ihlal eder. Örneğin, AutoValue'ın işleyiş şekliyle son değer sınıflarını veya son oluşturucuları uygulamanın bir yolu yoktur.
Kod stili
Bu kategori, geliştiricilerin özellikle herkese açık API'ler yazarken kullanması gereken genel kod stiliyle ilgilidir.
Belirtilenler hariç standart kodlama kurallarına uyun
Kuruluş dışından katkıda bulunanlar için Android kodlama kuralları burada açıklanmıştır:
https://source.android.com/source/code-style.html
Genel olarak, standart Java ve Kotlin kodlama kurallarına uymaya çalışıyoruz.
Kısaltmalar, yöntem adlarında büyük harfle yazılmamalıdır.
Örneğin: yöntem adı runCTSTests
değil runCtsTests
olmalıdır.
Adlar Impl ile bitmemelidir.
Bu, uygulama ayrıntılarını gösterir. Bunu yapmamaya çalışın.
Sınıflar
Bu bölümde sınıflar, arayüzler ve devralma ile ilgili kurallar açıklanmaktadır.
Uygun temel sınıftan yeni herkese açık sınıflar devralın
Kalıtım, alt sınıfınızda uygun olmayabilecek API öğelerini gösterir.
Örneğin, FrameLayout
sınıfının yeni herkese açık alt sınıfı, FrameLayout
sınıfına ek olarak yeni davranışlar ve API öğelerini içerir. Devralınan API, kullanım alanınız için uygun değilse ağacın daha üst kısmındaki bir sınıftan (ör. ViewGroup
veya View
) devralın.
UnsupportedOperationException
hatası oluşturmak için temel sınıftaki yöntemleri geçersiz kılmak istiyorsanız kullandığınız temel sınıfı yeniden değerlendirin.
Temel koleksiyon sınıflarını kullanma
Bir koleksiyonu bağımsız değişken olarak alırken veya değer olarak döndürürken daima temel sınıfı belirli uygulamaya tercih edin (ör. ArrayList<Foo>
yerine List<Foo>
döndürme).
API için uygun kısıtlamaları ifade eden bir temel sınıf kullanın. Örneğin, koleksiyonu sıralanması gereken bir API için List
, koleksiyonu benzersiz öğelerden oluşması gereken bir API için Set
kullanın.
Kotlin'de, değişmez koleksiyonları tercih edin. Daha fazla bilgi için Koleksiyonların değişebilirliği bölümüne bakın.
Soyut sınıflar ve arayüzler
Java 8, varsayılan arayüz yöntemleri için destek ekler. Bu sayede API tasarımcıları, ikili uyumluluğu korurken arayüzlere yöntem ekleyebilir. Platform kodu ve tüm Jetpack kitaplıkları Java 8 veya sonraki sürümleri hedeflemelidir.
Varsayılan uygulamanın durumsuz olduğu durumlarda API tasarımcıları, soyut sınıflar yerine arayüzleri tercih etmelidir. Yani varsayılan arayüz yöntemleri, diğer arayüz yöntemlerine yapılan çağrılar olarak uygulanabilir.
Varsayılan uygulama için bir kurucu veya dahili durum gerektiğinde soyut sınıflar kullanılmalıdır.
Her iki durumda da API tasarımcıları, lambda olarak kullanımı basitleştirmek için tek bir yöntemi soyut bırakmayı seçebilir:
public interface AnimationEndCallback {
// Always called, must be implemented.
public void onFinished(Animation anim);
// Optional callbacks.
public default void onStopped(Animation anim) { }
public default void onCanceled(Animation anim) { }
}
Sınıf adları, genişlettikleri sınıfları yansıtmalıdır
Örneğin, Service
sınıfını genişleten sınıflar, netlik için FooService
olarak adlandırılmalıdır:
public class IntentHelper extends Service {}
public class IntentService extends Service {}
Genel ekler
Yardımcı yöntem koleksiyonları için Helper
ve Util
gibi genel sınıf adı son ekleri kullanmaktan kaçının. Bunun yerine, yöntemleri doğrudan ilişkili sınıflara veya Kotlin uzantı işlevlerine yerleştirin.
Yöntemlerin birden fazla sınıfı birbirine bağladığı durumlarda, kapsayıcı sınıfa ne yaptığını açıklayan anlamlı bir ad verin.
Çok sınırlı durumlarda Helper
son ekini kullanmak uygun olabilir:
- Varsayılan davranışın oluşturulması için kullanılır
- Mevcut davranışın yeni sınıflara devredilmesi söz konusu olabilir
- Kalıcı durum gerekebilir
- Genellikle
View
ile ilgilidir
Örneğin, ipuçları geriye aktarılırken View
ile ilişkili durumun kalıcı hale getirilmesi ve geriye aktarma işlemini yüklemek için View
üzerinde çeşitli yöntemlerin çağrılması gerekiyorsa TooltipHelper
kabul edilebilir bir sınıf adı olur.
IDL tarafından oluşturulan kodu doğrudan herkese açık API'ler olarak göstermeyin
IDL tarafından oluşturulan kodu uygulama ayrıntıları olarak saklayın. Buna protobuf, soketler, FlatBuffers veya Java ya da NDK API'si olmayan diğer tüm API yüzeyleri dahildir. Ancak Android'deki IDL'lerin çoğu AIDL'de olduğundan bu sayfada AIDL ele alınmaktadır.
Oluşturulan AIDL sınıfları API stil kılavuzu şartlarını karşılamıyor (ör. aşırı yükleme kullanamıyor) ve AIDL aracı, dil API uyumluluğunu korumak için özel olarak tasarlanmadığından bu sınıfları herkese açık bir API'ye yerleştiremezsiniz.
Bunun yerine, başlangıçta basit bir sarmalayıcı olsa bile AIDL arayüzünün üzerine herkese açık bir API katmanı ekleyin.
Ciltleme arayüzleri
Binder
arayüzü bir uygulama ayrıntısıysa gelecekte herkese açık katman sayesinde gerekli geriye dönük uyumluluğun korunması koşuluyla özgürce değiştirilebilir. Örneğin, dahili çağrılara yeni bağımsız değişkenler eklemeniz veya paylaşılan bellek gibi bir yöntem kullanarak toplu işleme veya akış kullanarak IPC trafiğini optimize etmeniz gerekebilir. AIDL arayüzünüz aynı zamanda herkese açık API ise bunların hiçbiri yapılamaz.
Örneğin, FooService
'ü doğrudan herkese açık bir API olarak göstermeyin:
// BAD: Public API generated from IFooService.aidl
public class IFooService {
public void doFoo(String foo);
}
Bunun yerine, Binder
arayüzünü bir yöneticinin veya başka bir sınıfın içine sarın:
/**
* @hide
*/
public class IFooService {
public void doFoo(String foo);
}
public IFooManager {
public void doFoo(String foo) {
mFooService.doFoo(foo);
}
}
Daha sonra bu çağrı için yeni bir bağımsız değişkene ihtiyaç duyulursa dahili arayüz minimal olabilir ve herkese açık API'ye uygun aşırı yüklemeler eklenebilir. Uygulama geliştikçe geriye dönük uyumlulukla ilgili diğer sorunları ele almak için sarmalayıcı katmanı kullanabilirsiniz:
/**
* @hide
*/
public class IFooService {
public void doFoo(String foo, int flags);
}
public IFooManager {
public void doFoo(String foo) {
if (mAppTargetSdkLevel < 26) {
useOldFooLogic(); // Apps targeting API before 26 are broken otherwise
mFooService.doFoo(foo, FLAG_THAT_ONE_WEIRD_HACK);
} else {
mFooService.doFoo(foo, 0);
}
}
public void doFoo(String foo, int flags) {
mFooService.doFoo(foo, flags);
}
}
Android platformunun bir parçası olmayan Binder
arayüzleri (ör. uygulamaların kullanması için Google Play Hizmetleri tarafından dışa aktarılan bir hizmet arayüzü) için kararlı, yayınlanmış ve sürümlendirilmiş bir IPC arayüzü şartı, arayüzün kendisini geliştirmenin çok daha zor olduğu anlamına gelir. Ancak, diğer API yönergelerine uymak ve gerekirse IPC arayüzünün yeni bir sürümü için aynı herkese açık API'yi kullanmayı kolaylaştırmak amacıyla bu API'nin etrafında bir sarmalayıcı katmanı bulundurmak yine de faydalıdır.
Herkese açık API'de ham Binder nesneleri kullanmayın
Binder
nesnesi tek başına anlamlı olmadığından herkese açık API'de kullanılmamalıdır. Kimlik semantiği içerdiği için jeton olarak Binder
veya IBinder
kullanmak yaygın bir kullanım alanıdır. Ham Binder
nesnesi yerine bir sarmalayıcı jeton sınıfı kullanın.
public final class IdentifiableObject {
public Binder getToken() {...}
}
public final class IdentifiableObjectToken {
/**
* @hide
*/
public Binder getRawValue() {...}
/**
* @hide
*/
public static IdentifiableObjectToken wrapToken(Binder rawValue) {...}
}
public final class IdentifiableObject {
public IdentifiableObjectToken getToken() {...}
}
Yönetici sınıfları nihai olmalıdır
Yönetici sınıfları final
olarak tanımlanmalıdır. Yönetici sınıfları, sistem hizmetleriyle iletişim kurar ve tek etkileşim noktasıdır. Özelleştirmeye gerek yoktur. Bu nedenle, final
olarak beyan edin.
CompletableFuture veya Future kullanmayın
java.util.concurrent.CompletableFuture
, gelecek değerinin keyfi olarak değiştirilmesine izin veren ve hataya açık varsayılan değerlere sahip büyük bir API yüzeyine sahiptir.
Buna karşılık, java.util.concurrent.Future
'te engellenmeyen dinleme eksik olduğundan eşzamansız kodla kullanılmasını zorlaştırır.
Platform kodunda ve hem Kotlin hem de Java tarafından kullanılan düşük düzey kitaplık API'lerinde, tamamlama geri çağırma işlevinin Executor
ve API'nin iptal işlemini desteklemesi durumunda CancellationSignal
bir kombinasyonunu tercih edin.
public void asyncLoadFoo(android.os.CancellationSignal cancellationSignal,
Executor callbackExecutor,
android.os.OutcomeReceiver<FooResult, Throwable> callback);
Kotlin'i hedefliyorsanız suspend
işlevlerini tercih edin.
suspend fun asyncLoadFoo(): Foo
Java'ya özgü entegrasyon kitaplıklarında Guava'nın ListenableFuture
sınıfını kullanabilirsiniz.
public com.google.common.util.concurrent.ListenableFuture<Foo> asyncLoadFoo();
İsteğe bağlı seçeneğini kullanmayın
Optional
bazı API platformlarında avantajlara sahip olsa da mevcut Android API platform alanıyla tutarlı değildir. @Nullable
ve @NonNull
, null
güvenliği için araç yardımları sağlar ve Kotlin, boşluk sözleşmelerini derleyici düzeyinde uygulayarak Optional
'ı gereksiz kılar.
İsteğe bağlı primitifler için eşlenen has
ve get
yöntemlerini kullanın. Değer ayarlanmamışsa (has
false
döndürür) get
yöntemi bir IllegalStateException
atmalıdır.
public boolean hasAzimuth() { ... }
public int getAzimuth() {
if (!hasAzimuth()) {
throw new IllegalStateException("azimuth is not set");
}
return azimuth;
}
Oluşturulamayacak sınıflar için özel yapıcıları kullanma
Yalnızca Builder
tarafından oluşturulabilen sınıflar, yalnızca sabitler veya statik yöntemler içeren sınıflar ya da başka bir şekilde oluşturulamayan sınıflar, varsayılan parametresiz kurucu kullanılarak oluşturulmayı önlemek için en az bir özel kurucu içermelidir.
public final class Log {
// Not instantiable.
private Log() {}
}
Singleton
Testle ilgili aşağıdaki dezavantajları olduğundan tekil sınıflardan kaçınılması önerilir:
- Oluşturma işlemi sınıf tarafından yönetilir ve sahte içeriklerin kullanılması önlenir.
- Testler, tekil nesnenin statik yapısı nedeniyle hermetik olamaz
- Bu sorunları gidermek için geliştiricilerin tekil nesnenin dahili ayrıntılarını bilmesi veya etrafında bir sarmalayıcı oluşturması gerekir.
Bu sorunları gidermek için soyut bir temel sınıf kullanan tek örnek modelini tercih edin.
Tek örnek
Tek örnekli sınıflar, private
veya internal
kurucusu olan soyut bir temel sınıf kullanır ve örnek elde etmek için statik bir getInstance()
yöntemi sağlar. getInstance()
yöntemi, sonraki çağrılarda aynı nesneyi döndürmelidir.
getInstance()
tarafından döndürülen nesne, soyut temel sınıfın özel bir uygulaması olmalıdır.
class Singleton private constructor(...) {
companion object {
private val _instance: Singleton by lazy { Singleton(...) }
fun getInstance(): Singleton {
return _instance
}
}
}
abstract class SingleInstance private constructor(...) {
companion object {
private val _instance: SingleInstance by lazy { SingleInstanceImp(...) }
fun getInstance(): SingleInstance {
return _instance
}
}
}
Tek örnek, geliştiricilerin SingleInstance
'nin sahte bir sürümünü oluşturabilmesi ve sarmalayıcı oluşturmak zorunda kalmadan uygulamayı yönetmek için kendi Bağımlılık Enjeksiyonu çerçevesini kullanabilmesi veya kitaplığın bir -testing
yapıtında kendi sahte örneğini sağlayabilmesi açısından tekil öğeden farklıdır.
Kaynakları serbest bırakan sınıflar AutoCloseable'ı uygulamalıdır.
close
, release
, destroy
veya benzer yöntemler aracılığıyla kaynak yayınlayan sınıflar, geliştiricilerin try-with-resources
bloğu kullanırken bu kaynakları otomatik olarak temizlemesine olanak tanımak için java.lang.AutoCloseable
'ı uygulamalıdır.
Android'de yeni View alt sınıfları tanıtmaktan kaçının.*
Platformun herkese açık API'sinde (yani android.*
içinde) doğrudan veya dolaylı olarak android.view.View
sınıfından devralan yeni sınıflar eklemeyin.
Android'in kullanıcı arayüzü araç kiti artık önce Compose'a odaklanmıştır. Platform tarafından sunulan yeni kullanıcı arayüzü özellikleri, Jetpack kitaplıklarında geliştiriciler için Jetpack Compose'u ve isteğe bağlı olarak görüntü tabanlı kullanıcı arayüzü bileşenlerini uygulamak üzere kullanılabilecek alt düzey API'ler olarak sunulmalıdır. Bu bileşenleri kitaplıklarda sunmak, platform özellikleri kullanılamadığında geriye dönük uygulama fırsatları sunar.
Fields'ın oynadığı filmler
Bu kurallar, sınıflardaki herkese açık alanlarla ilgilidir.
Ham alanları göstermeyin
Java sınıfları alanları doğrudan göstermemelidir. Alanlar özel olmalı ve nihai olup olmadıklarından bağımsız olarak yalnızca herkese açık alıcı ve ayarlayıcılar kullanılarak erişilebilir olmalıdır.
Bir alanı belirtme veya getirme davranışının iyileştirilmesine gerek olmayan temel veri yapıları, istisnalar arasındadır. Bu gibi durumlarda, alanlar standart değişken adlandırma kuralları (ör. Point.x
ve Point.y
) kullanılarak adlandırılmalıdır.
Kotlin sınıfları mülkleri gösterebilir.
Açıklanan alanlar nihai olarak işaretlenmelidir
Ham alanların kullanılması kesinlikle önerilmez (@see
Ham alanları göstermeyin). Ancak bir alanın herkese açık alan olarak gösterildiği nadir durumlarda bu alanı final
olarak işaretleyin.
Dahili alanlar gösterilmemelidir
Herkese açık API'de dahili alan adlarına referans vermeyin.
public int mFlags;
Koruma altında yerine herkese açık kullanın
@bkz "Korunan" yerine "Herkese açık"ı kullanma
Sabitler
Bunlar, herkese açık sabitlerle ilgili kurallardır.
İşaret sabitleri, int veya long değerleriyle çakışmamalıdır
İşaretler, birleştirme değeriyle birleştirilebilecek bitleri ifade eder. Aksi takdirde, değişkeni veya sabit değeri flag
olarak çağırmayın.
public static final int FLAG_SOMETHING = 2;
public static final int FLAG_SOMETHING = 3;
public static final int FLAG_PRIVATE = 1 << 2;
public static final int FLAG_PRESENTATION = 1 << 3;
Herkese açık işaret sabitlerini tanımlama hakkında daha fazla bilgi için @IntDef
bit maskesi işaretleri bölümüne bakın.
statik final sabitler büyük harfli ve alt çizgiyle ayrılmış adlandırma kuralını kullanmalıdır
Sabitte yer alan tüm kelimeler büyük harfle yazılmalı ve birden fazla kelime _
ile ayrılmalıdır. Örneğin:
public static final int fooThing = 5
public static final int FOO_THING = 5
Sabitler için standart ön ekleri kullanın
Android'de kullanılan sabitlerin çoğu işaretler, anahtarlar ve işlemler gibi standart öğeler içindir. Bu sabitlerin, bu öğeler olarak daha kolay tanınması için standart ön eklerine sahip olması gerekir.
Örneğin, intent ekstraları EXTRA_
ile başlamalıdır. Amaç işlemleri ACTION_
ile başlamalıdır. Context.bindService()
ile kullanılan sabitler BIND_
ile başlamalıdır.
Anahtar sabit adları ve kapsamları
Dize sabit değerleri, sabit adın kendisiyle tutarlı olmalı ve genellikle paket veya alan kapsamına alınmalıdır. Örneğin:
public static final String FOO_THING = "foo"
tutarlı bir şekilde adlandırılmamış veya uygun şekilde kapsamlandırılmamış. Bunun yerine şunları yapabilirsiniz:
public static final String FOO_THING = "android.fooservice.FOO_THING"
Kapsamlı dize sabitlerindeki android
ön ekleri Android Açık Kaynak Projesi için ayrılmıştır.
Intent işlemleri ve ekstralar ile Bundle girişleri, tanımlandıkları paket adı kullanılarak ad alanına eklenmelidir.
package android.foo.bar {
public static final String ACTION_BAZ = "android.foo.bar.action.BAZ"
public static final String EXTRA_BAZ = "android.foo.bar.extra.BAZ"
}
Koruma altında yerine herkese açık kullanın
@bkz "Korunan" yerine "Herkese açık"ı kullanma
Tutarlı ön ekler kullanın
İlgili sabitlerin tümü aynı ön ekiyle başlamalıdır. Örneğin, işaret değerleriyle kullanılacak bir sabit değer grubu için:
public static final int SOME_VALUE = 0x01;
public static final int SOME_OTHER_VALUE = 0x10;
public static final int SOME_THIRD_VALUE = 0x100;
public static final int FLAG_SOME_VALUE = 0x01;
public static final int FLAG_SOME_OTHER_VALUE = 0x10;
public static final int FLAG_SOME_THIRD_VALUE = 0x100;
@see Sabit değerler için standart ön ekler kullanma
Tutarlı kaynak adları kullanın
Herkese açık tanımlayıcılar, özellikler ve değerler, Java'daki herkese açık alanlara benzer şekilde @id/accessibilityActionPageUp
veya @attr/textAppearance
gibi camelCase adlandırma kuralı kullanılarak adlandırılmalıdır.
Bazı durumlarda, herkese açık tanımlayıcı veya özellik, alt çizgiyle ayrılmış ortak bir önek içerir:
- config.xml dosyasında
@string/config_recentsComponentName
gibi platform yapılandırma değerleri - attrs.xml dosyasında
@attr/layout_marginStart
gibi sayfa düzenine özgü görüntüleme özellikleri
Herkese açık temalar ve stiller, Java'daki iç içe yerleştirilmiş sınıflara benzer şekilde hiyerarşik PascalCase adlandırma kuralına (ör. @style/Theme.Material.Light.DarkActionBar
veya @style/Widget.Material.SearchView.ActionBar
) uymalıdır.
Düzen ve çizilebilir kaynaklar, herkese açık API'ler olarak sunulmamalıdır. Ancak bu öğeler gösterilmek zorundaysa herkese açık düzenler ve çizilebilir öğeler, alt çizgi adlandırma kuralı kullanılarak adlandırılmalıdır (ör. layout/simple_list_item_1.xml
veya drawable/title_bar_tall.xml
).
Sabitler değişebileceğinde bunları dinamik hale getirin
Derleyici, sabit değerleri satır içi olarak yerleştirebilir. Bu nedenle, değerlerin aynı kalması API sözleşmesinin bir parçası olarak kabul edilir. Bir MIN_FOO
veya MAX_FOO
sabitinin değeri gelecekte değişebilirse bunun yerine dinamik yöntemler oluşturmayı düşünebilirsiniz.
CameraManager.MAX_CAMERAS
CameraManager.getMaxCameras()
Geri aramalar için ileriye dönük uyumluluğu göz önünde bulundurun
Gelecekteki API sürümlerinde tanımlanan sabitler, eski API'leri hedefleyen uygulamalar tarafından bilinmez. Bu nedenle, uygulamalara iletilen sabitler uygulamanın hedef API sürümünü dikkate almalı ve yeni sabitleri tutarlı bir değerle eşlemelidir. Aşağıdaki senaryoyu düşünün:
Varsayımsal SDK kaynağı:
// Added in API level 22
public static final int STATUS_SUCCESS = 1;
public static final int STATUS_FAILURE = 2;
// Added in API level 23
public static final int STATUS_FAILURE_RETRY = 3;
// Added in API level 26
public static final int STATUS_FAILURE_ABORT = 4;
targetSdkVersion="22"
içeren varsayımsal uygulama:
if (result == STATUS_FAILURE) {
// Oh no!
} else {
// Success!
}
Bu durumda uygulama, API düzeyi 22'nin kısıtlamaları dahilinde tasarlandı ve yalnızca iki olası durum olduğu (bir dereceye kadar) makul bir varsayım yapıldı. Ancak uygulama yeni eklenen STATUS_FAILURE_RETRY
değerini alırsa bunu başarı olarak yorumlar.
Sabit döndüren yöntemler, çıktılarını uygulamanın hedeflediği API düzeyiyle eşleşecek şekilde kısıtlayarak bu tür durumları güvenli bir şekilde ele alabilir:
private int mapResultForTargetSdk(Context context, int result) {
int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
if (targetSdkVersion < 26) {
if (result == STATUS_FAILURE_ABORT) {
return STATUS_FAILURE;
}
if (targetSdkVersion < 23) {
if (result == STATUS_FAILURE_RETRY) {
return STATUS_FAILURE;
}
}
}
return result;
}
Geliştiriciler, sabitlerin listesinin gelecekte değişip değişmeyeceğini tahmin edemez. Her şeyi kapsayan bir UNKNOWN
veya UNSPECIFIED
sabitiyle bir API tanımlarsanız geliştiriciler, uygulamalarını yazarken yayınlanan sabitlerin kapsamlı olduğunu varsayarlar. Bu beklentiyi belirlemek istemiyorsanız her şeyi kapsayan bir sabitin API'niz için iyi bir fikir olup olmadığını yeniden değerlendirin.
Ayrıca, kitaplıklar kendi targetSdkVersion
'lerini uygulamadan ayrı olarak belirtemez ve kitaplık kodundan targetSdkVersion
davranış değişikliklerini yönetmek karmaşık ve hatalara açıktır.
Tam sayı veya dize sabiti
Değerlerin ad alanı paketinizin dışında genişletilemiyorsa tam sayı sabitleri ve @IntDef
kullanın. Ad alanı paylaşılıyorsa veya paketinizin dışındaki kodla genişletilebiliyorsa dize sabitleri kullanın.
Veri sınıfları
Veri sınıfları, değiştirilemeyen bir dizi özelliği temsil eder ve bu verilerle etkileşim kurmak için küçük ve iyi tanımlanmış bir yardımcı program işlevi grubu sağlar.
Kotlin derleyicisi, oluşturulan kod için dil API'si veya ikili uyumluluğu garanti etmediğinden, herkese açık Kotlin API'lerinde data class
'yi kullanmayın. Bunun yerine, gerekli işlevleri manuel olarak uygulayın.
Örneklendirme
Java'da veri sınıfları, az sayıda özellik olduğunda bir kurucu sağlamalıdır veya çok sayıda özellik olduğunda Builder
kalıbını kullanmalıdır.
Kotlin'de veri sınıfları, özelliklerin sayısına bakılmaksızın varsayılan bağımsız değişkenler içeren bir kurucu sağlamalıdır. Kotlin'de tanımlanan veri sınıfları, Java istemcileri hedeflenirken bir oluşturucu sağlanmasından da yararlanabilir.
Değişiklik ve kopyalama
Verilerin değiştirilmesi gerektiğinde, kopya oluşturucu içeren bir Builder
sınıfı (Java) veya yeni bir nesne döndüren bir copy()
üye işlevi (Kotlin) sağlayın.
Kotlin'de copy()
işlevi sağlarken bağımsız değişkenler sınıfın kurucusuyla eşleşmeli ve varsayılan değerler, nesnenin mevcut değerleri kullanılarak doldurulmalıdır:
class Typography(
val labelMedium: TextStyle = TypographyTokens.LabelMedium,
val labelSmall: TextStyle = TypographyTokens.LabelSmall
) {
fun copy(
labelMedium: TextStyle = this.labelMedium,
labelSmall: TextStyle = this.labelSmall
): Typography = Typography(
labelMedium = labelMedium,
labelSmall = labelSmall
)
}
Ek davranışlar
Veri sınıfları hem equals()
hem de hashCode()
'i uygulamalıdır ve bu yöntemlerin uygulamalarında her mülk dikkate alınmalıdır.
Veri sınıfları, Kotlin'in veri sınıfı uygulamasına (ör. User(var1=Alex, var2=42)
) uygun önerilen bir biçimle toString()
uygulayabilir.
Yöntemler
Bunlar, yöntemlerdeki çeşitli ayrıntılar, parametreler, yöntem adları, döndürülen tür ve erişim belirteçleriyle ilgili kurallardır.
Süre
Bu kurallar, tarih ve süre gibi zaman kavramlarının API'lerde nasıl ifade edilmesi gerektiğini kapsar.
Mümkün olduğunda java.time.* türlerini tercih edin
java.time.Duration
, java.time.Instant
ve diğer birçok java.time.*
türü, şekillendirmeyi kaldırma işlemiyle tüm platform sürümlerinde kullanılabilir ve API parametrelerinde veya döndürülen değerlerde zamanı ifade ederken tercih edilmelidir.
API alanının, amaçlanan kullanım kalıplarında nesne tahsisinin performansı olumsuz yönde etkileyeceği bir alan olmadığı sürece, API'nin yalnızca java.time.Duration
veya java.time.Instant
kabul eden ya da döndüren varyantlarını göstermeyi tercih edin ve aynı kullanım alanına sahip ilkel varyantları çıkarın.
Süreleri ifade eden yöntemler duration olarak adlandırılmalıdır.
Bir zaman değeri, ilgili süreyi ifade ediyorsa parametreyi "time" değil, "duration" olarak adlandırın.
ValueAnimator.setTime(java.time.Duration);
ValueAnimator.setDuration(java.time.Duration);
İstisnalar:
Süre özellikle bir zaman aşımı değeri için geçerli olduğunda "timeout" uygundur.
java.time.Instant
türüne sahip "time", süre yerine belirli bir zaman noktasını belirtmek için uygundur.
Süreleri veya zamanı temel öğe olarak ifade eden yöntemler, zaman birimleriyle adlandırılmalı ve uzun
Süreleri ilkel olarak kabul eden veya döndüren yöntemler, süslenmemiş adı java.time.Duration
ile kullanılmak üzere ayırmak için yöntem adına ilişkili zaman birimlerini (ör. Millis
, Nanos
, Seconds
) son ek olarak eklemelidir. Zaman başlıklı makaleyi inceleyin.
Yöntemler, birim ve zaman tabanlarıyla uygun şekilde ek açıklamayla belirtilmelidir:
@CurrentTimeMillisLong
: Değer, 1970-01-01T00:00:00Z'den itibaren geçen milisaniye sayısı olarak ölçülen sıfırdan büyük bir zaman damgası.@CurrentTimeSecondsLong
: Değer, 1970-01-01T00:00:00Z'den sonraki saniye sayısı olarak ölçülen sıfırdan büyük bir zaman damgası.@DurationMillisLong
: Değer, milisaniye cinsinden sıfırdan büyük bir süredir.@ElapsedRealtimeLong
: Değer,SystemClock.elapsedRealtime()
zaman tabanında sıfırdan büyük bir zaman damgası olmalıdır.@UptimeMillisLong
: Değer,SystemClock.uptimeMillis()
zaman tabanında sıfırdan büyük bir zaman damgası olmalıdır.
Basit zaman parametreleri veya döndürülen değerler int
yerine long
kullanmalıdır.
ValueAnimator.setDuration(@DurationMillisLong long);
ValueAnimator.setDurationNanos(long);
Zaman birimlerini ifade eden yöntemlerde, birim adları için kısaltılmamış kısaltmalar tercih edilmelidir.
public void setIntervalNs(long intervalNs);
public void setTimeoutUs(long timeoutUs);
public void setIntervalNanos(long intervalNanos);
public void setTimeoutMicros(long timeoutMicros);
Uzun zaman argümanlarına not ekleme
Platform, long
türündeki zaman birimleri için daha güçlü bir yazma deneyimi sunmak üzere çeşitli ek açıklamalar içerir:
@CurrentTimeMillisLong
: Değer,1970-01-01T00:00:00Z
'den bu yana geçen milisaniye sayısı olarak ölçülen ve dolayısıylaSystem.currentTimeMillis()
zaman tabanında olan sıfırdan büyük bir zaman damgası.@CurrentTimeSecondsLong
: Değer,1970-01-01T00:00:00Z
tarihinden itibaren geçen saniye sayısı olarak ölçülen sıfırdan büyük bir zaman damgası.@DurationMillisLong
: Değer, milisaniye cinsinden sıfırdan büyük bir süredir.@ElapsedRealtimeLong
: Değer,SystemClock#elapsedRealtime()
zaman tabanında sıfırdan büyük bir zaman damgası olmalıdır.@UptimeMillisLong
: Değer,SystemClock#uptimeMillis()
zaman tabanında sıfırdan büyük bir zaman damgası olmalıdır.
Ölçü birimleri
Zaman dışında bir ölçü birimini ifade eden tüm yöntemler için CamelCase biçimindeki SI birim ön eklerine öncelik verin.
public long[] getFrequenciesKhz();
public float getStreamVolumeDb();
İsteğe bağlı parametreleri aşırı yüklemelerin sonuna koyun
İsteğe bağlı parametreler içeren bir yöntemin aşırı yüklemeleri varsa bu parametreleri en sonda tutun ve diğer parametrelerle tutarlı bir sıralama yapın:
public int doFoo(boolean flag);
public int doFoo(int id, boolean flag);
public int doFoo(boolean flag);
public int doFoo(boolean flag, int id);
İsteğe bağlı bağımsız değişkenler için aşırı yükleme eklerken daha basit yöntemlerin davranışı, daha ayrıntılı yöntemlere varsayılan bağımsız değişkenler sağlanmış gibi tam olarak aynı şekilde olmalıdır.
Sonuç: İsteğe bağlı bağımsız değişkenler eklemek veya yöntem polimorfikse farklı türde bağımsız değişkenler kabul etmek dışında yöntemlere aşırı yükleme yapmayın. Aşırı yüklenen yöntem temelde farklı bir şey yapıyorsa yönteme yeni bir ad verin.
Varsayılan parametreleri olan yöntemler @JvmOverloads ile ek açıklamaya tabi tutulmalıdır (yalnızca Kotlin)
Varsayılan parametreleri olan yöntemler ve kurucular, ikili uyumluluğu korumak için @JvmOverloads
ile ek açıklamaya tabi tutulmalıdır.
Daha fazla bilgi için resmi Kotlin-Java birlikte çalışabilirlik kılavuzundaki Varsayılanlar için işlev aşırı yüklemeleri bölümüne bakın.
class Greeting @JvmOverloads constructor(
loudness: Int = 5
) {
@JvmOverloads
fun sayHello(prefix: String = "Dr.", name: String) = // ...
}
Varsayılan parametre değerlerini kaldırmayın (yalnızca Kotlin)
Bir yöntem, varsayılan değere sahip bir parametreyle birlikte yayınlandıysa varsayılan değerin kaldırılması kaynakta değişiklik yapılmasına neden olur.
En ayırt edici ve tanımlayıcı yöntem parametreleri önce gelmelidir
Birden fazla parametresi olan bir yönteminiz varsa en alakalı olanları öne çıkarın. İşaretleri ve diğer seçenekleri belirten parametreler, işlem yapılan nesneyi tanımlayan parametrelere kıyasla daha az önemlidir. Tamamlama geri çağırma işlevi varsa bunu sona koyun.
public void openFile(int flags, String name);
public void openFileAsync(OnFileOpenedListener listener, String name, int flags);
public void setFlags(int mask, int flags);
public void openFile(String name, int flags);
public void openFileAsync(String name, int flags, OnFileOpenedListener listener);
public void setFlags(int flags, int mask);
Ayrıca bkz. Aşırı yüklemelerde isteğe bağlı parametreleri sona koyma
İnşaat Ustaları
Karmaşık Java nesneleri oluşturmak için Builder kalıbı önerilir ve Android'de aşağıdaki durumlarda yaygın olarak kullanılır:
- Ortaya çıkan nesnenin özellikleri sabit olmalıdır.
- Çok sayıda zorunlu özellik vardır (ör. birçok kurucu bağımsız değişkeni)
- Oluşturma sırasında mülkler arasında karmaşık bir ilişki vardır. Örneğin, bir doğrulama adımı gerekir. Bu karmaşıklık düzeyinin genellikle API'nin kullanılabilirliğiyle ilgili sorunlara işaret ettiğini unutmayın.
Bir geliştiriciye ihtiyacınız olup olmadığını düşünün. Oluşturucular, API yüzeyinde aşağıdaki amaçlarla kullanılırsa faydalıdır:
- Olası büyük bir isteğe bağlı oluşturma parametresi grubundan yalnızca birkaçını yapılandırma
- Bazen benzer veya eşleşen türlerde olan birçok farklı isteğe bağlı veya zorunlu oluşturma parametresini yapılandırın. Aksi takdirde, çağrı sitelerinin okunması kafa karıştırıcı hale gelebilir veya yazılması hatalara yol açabilir.
- Bir nesnenin oluşturulmasını kademeli olarak yapılandırın. Bu durumda, birkaç farklı yapılandırma kodu parçası, uygulama ayrıntıları olarak oluşturucuda çağrılar yapabilir.
- Gelecekteki API sürümlerinde ek isteğe bağlı oluşturma parametreleri ekleyerek bir türün büyümesine izin verme
Üç veya daha az zorunlu parametre ve isteğe bağlı parametre içermeyen bir türünüz varsa neredeyse her zaman bir oluşturucuyu atlayabilir ve düz bir kurucu kullanabilirsiniz.
Kotlin kaynaklı sınıflar, varsayılan bağımsız değişkenlerle @JvmOverloads
ile ek açıklamalı yapıcıları tercih etmelidir ancak daha önce belirtilen durumlarda yapıcılar da sağlayarak Java istemcileri için kullanılabilirliği artırmayı seçebilir.
class Tone @JvmOverloads constructor(
val duration: Long = 1000,
val frequency: Int = 2600,
val dtmfConfigs: List<DtmfConfig> = emptyList()
) {
class Builder {
// ...
}
}
Oluşturucu sınıfları, oluşturucuyu döndürmelidir
Builder sınıfları, build()
hariç her yöntemden Builder nesnesini (this
gibi) döndürerek yöntem zincirleme özelliğini etkinleştirmelidir. Ek oluşturulmuş nesneler bağımsız değişken olarak iletilmelidir. Farklı bir nesnenin oluşturucusunu döndürmeyin.
Örneğin:
public static class Builder {
public void setDuration(long);
public void setFrequency(int);
public DtmfConfigBuilder addDtmfConfig();
public Tone build();
}
public class Tone {
public static class Builder {
public Builder setDuration(long);
public Builder setFrequency(int);
public Builder addDtmfConfig(DtmfConfig);
public Tone build();
}
}
Bir temel oluşturucu sınıfının uzantıyı desteklemesi gereken nadir durumlarda genel bir döndürme türü kullanın:
public abstract class Builder<T extends Builder<T>> {
abstract T setValue(int);
}
public class TypeBuilder<T extends TypeBuilder<T>> extends Builder<T> {
T setValue(int);
T setTypeSpecificValue(long);
}
Oluşturucu sınıfları bir oluşturucu aracılığıyla oluşturulmalıdır
Android API yüzeyi üzerinden tutarlı bir oluşturucu oluşturma işlemini sürdürmek için tüm oluşturucular, statik bir oluşturucu yöntemi yerine bir kurucu aracılığıyla oluşturulmalıdır. Kotlin tabanlı API'ler için, Kotlin kullanıcılarının bir fabrika yöntemi/DSL stili oluşturma mekanizması aracılığıyla dolaylı olarak oluşturucuya güvenmesi beklense bile Builder
herkese açık olmalıdır. Kitaplıklar, Builder
sınıf kurucusunu Kotlin istemcilerinden seçerek gizlemek için @PublishedApi internal
'yi kullanmamalıdır.
public class Tone {
public static Builder builder();
public static class Builder {
}
}
public class Tone {
public static class Builder {
public Builder();
}
}
Oluşturucu oluşturucularına gönderilen tüm bağımsız değişkenler zorunlu olmalıdır (ör. @NonNull).
İsteğe bağlı bağımsız değişkenler (ör. @Nullable
) ayarlayıcı yöntemlerine taşınmalıdır.
Gerekli bağımsız değişkenler belirtilmezse oluşturucu bir NullPointerException
(Objects.requireNonNull
kullanmayı düşünebilirsiniz) hatası atmalıdır.
Oluşturucu sınıfları, oluşturulan türlerinin nihai statik iç sınıfları olmalıdır
Bir paket içindeki mantıksal düzenleme için oluşturucu sınıfları genellikle oluşturulan türlerinin nihai iç sınıfları olarak gösterilmelidir (örneğin, ToneBuilder
yerine Tone.Builder
).
Oluşturucular, mevcut bir örnekten yeni bir örnek oluşturmak için bir kurucu dahil edebilir
Oluşturucular, mevcut bir oluşturucudan veya oluşturulmuş nesneden yeni bir oluşturucu örneği oluşturmak için bir kopyalama kurucusu içerebilir. Mevcut oluşturuculardan veya oluşturma nesnelerinden oluşturucu örnekleri oluşturmak için alternatif yöntemler sunmamalıdır.
public class Tone {
public static class Builder {
public Builder clone();
}
public Builder toBuilder();
}
public class Tone {
public static class Builder {
public Builder(Builder original);
public Builder(Tone original);
}
}
Oluşturucuda kopya oluşturucu varsa oluşturucu ayarlayıcıları @Nullable bağımsız değişkenlerini almalıdır
Mevcut bir oluşturucu örneğinden yeni bir oluşturucu örneği oluşturulabilecekse sıfırlama işlemi gereklidir. Kopya oluşturucu yoksa oluşturucuda @Nullable
veya @NonNullable
bağımsız değişkeni olabilir.
public static class Builder {
public Builder(Builder original);
public Builder setObjectValue(@Nullable Object value);
}
Oluşturucu ayarlayıcıları, isteğe bağlı özellikler için @Nullable bağımsız değişkenleri alabilir
İkinci derece giriş için genellikle boş değer kullanmak daha kolaydır. Özellikle de Kotlin'de, oluşturucular ve aşırı yüklemeler yerine varsayılan bağımsız değişkenler kullanılır.
Ayrıca @Nullable
ayarlayıcılar, kendilerini isteğe bağlı özellikler için @Nullable
olması gereken alıcılarıyla eşleştirir.
Value createValue(@Nullable OptionalValue optionalValue) {
Value.Builder builder = new Value.Builder();
if (optionalValue != null) {
builder.setOptionalValue(optionalValue);
}
return builder.build();
}
Value createValue(@Nullable OptionalValue optionalValue) {
return new Value.Builder()
.setOptionalValue(optionalValue);
.build();
}
// Or in other cases:
Value createValue() {
return new Value.Builder()
.setOptionalValue(condition ? new OptionalValue() : null);
.build();
}
Kotlin'de yaygın kullanım:
fun createValue(optionalValue: OptionalValue? = null) =
Value.Builder()
.apply { optionalValue?.let { setOptionalValue(it) } }
.build()
fun createValue(optionalValue: OptionalValue? = null) =
Value.Builder()
.setOptionalValue(optionalValue)
.build()
Varsayılan değer (ayarlayıcı çağrılmazsa) ve null
'ün anlamı hem ayarlayıcıda hem de alıcıda uygun şekilde belgelenmelidir.
/**
* ...
*
* <p>Defaults to {@code null}, which means the optional value won't be used.
*/
Oluşturulan sınıfta ayarlayıcıların bulunduğu değişken özellikler için oluşturucu ayarlayıcılar sağlanabilir
Sınıfınızda değişken özellikler varsa ve Builder
sınıfına ihtiyacınız varsa öncelikle sınıfınızın gerçekten değişken özelliklere sahip olup olmadığını kendinize sorun.
Ardından, değişken özelliklere ihtiyacınız olduğundan eminseniz aşağıdaki senaryolardan hangisinin beklenen kullanım alanınız için daha uygun olduğuna karar verin:
Oluşturulan nesne hemen kullanılabilir olmalıdır. Bu nedenle, değişken veya değişmez olsun, ilgili tüm özellikler için ayarlayıcılar sağlanmalıdır.
map.put(key, new Value.Builder(requiredValue) .setImmutableProperty(immutableValue) .setUsefulMutableProperty(usefulValue) .build());
Oluşturulan nesnenin yararlı olabilmesi için bazı ek çağrılar yapılması gerekebilir. Bu nedenle, değiştirilebilir özellikler için ayarlayıcılar sağlanmamalıdır.
Value v = new Value.Builder(requiredValue) .setImmutableProperty(immutableValue) .build(); v.setUsefulMutableProperty(usefulValue) Result r = v.performSomeAction(); Key k = callSomeMethod(r); map.put(k, v);
İki senaryoyu karıştırmayın.
Value v = new Value.Builder(requiredValue)
.setImmutableProperty(immutableValue)
.setUsefulMutableProperty(usefulValue)
.build();
Result r = v.performSomeAction();
Key k = callSomeMethod(r);
map.put(k, v);
Oluşturucularda alıcı olmamalıdır
Geter, oluşturucuda değil, oluşturulan nesnede olmalıdır.
Oluşturucu ayarlayıcılarının, oluşturulan sınıfta karşılık gelen alıcıları olmalıdır
public class Tone {
public static class Builder {
public Builder setDuration(long);
public Builder setFrequency(int);
public Builder addDtmfConfig(DtmfConfig);
public Tone build();
}
}
public class Tone {
public static class Builder {
public Builder setDuration(long);
public Builder setFrequency(int);
public Builder addDtmfConfig(DtmfConfig);
public Tone build();
}
public long getDuration();
public int getFrequency();
public @NonNull List<DtmfConfig> getDtmfConfigs();
}
Derleyici yöntemi adlandırma
Oluşturucu yöntemi adlarında setFoo()
, addFoo()
veya clearFoo()
stili kullanılmalıdır.
Oluşturucu sınıflarının bir build() yöntemi tanımlaması gerekir.
Oluşturucu sınıfları, oluşturulan nesnenin bir örneğini döndüren bir build()
yöntemi tanımlamalıdır.
Builder build() yöntemleri @NonNull nesneler döndürmelidir
Bir oluşturucunun build()
yönteminin, oluşturulan nesnenin null olmayan bir örneğini döndürmesi beklenir. Geçersiz parametreler nedeniyle nesne oluşturulamazsa doğrulama, oluşturma yöntemine ertelenebilir ve bir IllegalStateException
oluşturulur.
Dahili kilitleri gösterme
Herkese açık API'deki yöntemler synchronized
anahtar kelimesini kullanmamalıdır. Bu anahtar kelime, nesnenizin veya sınıfınızın kilit olarak kullanılmasına neden olur. Diğer kullanıcılara açık olduğu için sınıfınızın dışındaki diğer kodlar kilitleme amacıyla kullanmaya başlarsa beklenmedik yan etkilerle karşılaşabilirsiniz.
Bunun yerine, gerekli kilitleme işlemlerini dahili ve özel bir nesneye karşı gerçekleştirin.
public synchronized void doThing() { ... }
private final Object mThingLock = new Object();
public void doThing() {
synchronized (mThingLock) {
...
}
}
Erişimci stilindeki yöntemler, Kotlin mülk yönergelerine uygun olmalıdır
Kotlin kaynaklarından görüntülendiğinde, get
, set
veya is
ön eklerinin kullanıldığı erişim tarzı yöntemler de Kotlin mülkleri olarak kullanılabilir.
Örneğin, Java'da tanımlanan int getField()
, Kotlin'de val field: Int
mülkü olarak kullanılabilir.
Bu nedenle ve genel olarak erişim yöntemi davranışıyla ilgili geliştirici beklentilerini karşılamak için erişim yöntemi ön eklerinin kullanıldığı yöntemler Java alanlarına benzer şekilde davranmalıdır. Aşağıdaki durumlarda erişimci stilinde ön ek kullanmaktan kaçının:
- Yöntemin yan etkileri var. Daha açıklayıcı bir yöntem adı tercih edin.
- Yöntem, hesaplama açısından pahalı bir çalışma içerir.
compute
'ü tercih edin. - Yöntem, IPC veya diğer G/Ç gibi bir değer döndürmek için uzun süren bir çalışmayı engellemeyi veya başka bir şekilde durdurmayı içerir.
fetch
- Yöntem, bir değer döndürene kadar ileti dizisini engeller.
await
değerini tercih edin. - Yöntem her çağrıda yeni bir nesne örneği döndürür.
create
- Yöntem bir değeri başarıyla döndürmeyebilir.
request
yöntemini tercih edin.
Hesaplama açısından pahalı bir işlemi bir kez gerçekleştirip sonraki çağrılar için değeri önbelleğe almanın, hesaplama açısından pahalı bir işlem gerçekleştirmek olarak değerlendirilmeye devam ettiğini unutmayın. Takılma, kareler arasında amorti edilmez.
Boole erişim yöntemleri için is ön ekini kullanma
Bu, Java'daki doğru/yanlış yöntemler ve alanlar için standart adlandırma kuralı. Genel olarak, boole yöntemi ve değişken adları, döndürülen değer tarafından yanıtlanan sorular olarak yazılmalıdır.
Java boole erişim yöntemleri set
/is
adlandırma şemasına uymalı ve alanlar is
'yi tercih etmelidir. Örneğin:
// Visibility is a direct property. The object "is" visible:
void setVisible(boolean visible);
boolean isVisible();
// Factory reset protection is an indirect property.
void setFactoryResetProtectionEnabled(boolean enabled);
boolean isFactoryResetProtectionEnabled();
final boolean isAvailable;
Java erişim yöntemleri için set
/is
veya Java alanları için is
kullanıldığında bu öğeler Kotlin'den özellik olarak kullanılabilir:
obj.isVisible = true
obj.isFactoryResetProtectionEnabled = false
if (!obj.isAvailable) return
Özellikler ve erişim yöntemleri genellikle olumlu adlandırma kullanmalıdır. Örneğin, Disabled
yerine Enabled
. Negatif terminoloji kullanmak, true
ve false
'un anlamını tersine çevirir ve davranışla ilgili akıl yürütmeyi zorlaştırır.
// Passing false here is a double-negative.
void setFactoryResetProtectionDisabled(boolean disabled);
Boole değerinin bir mülkün dahil edilmesini veya sahipliğini tanımladığı durumlarda is yerine has kullanabilirsiniz. Ancak bu, Kotlin mülk söz diziminde çalışmaz:
// Transient state is an indirect property used to track state
// related to the object. The object is not transient; rather,
// the object "has" transient state associated with it:
void setHasTransientState(boolean hasTransientState);
boolean hasTransientState();
Daha uygun olabilecek bazı alternatif ön ekler can ve should'dur:
// "Can" describes a behavior that the object may provide,
// and here is more concise than setRecordingEnabled or
// setRecordingAllowed. The object "can" record:
void setCanRecord(boolean canRecord);
boolean canRecord();
// "Should" describes a hint or property that is not strictly
// enforced, and here is more explicit than setFitWidthEnabled.
// The object "should" fit width:
void setShouldFitWidth(boolean shouldFitWidth);
boolean shouldFitWidth();
Davranışları veya özellikleri değiştiren yöntemler is ön ekiyle Enabled son ekiyle kullanılabilir:
// "Enabled" describes the availability of a property, and is
// more appropriate here than "can use" or "should use" the
// property:
void setWiFiRoamingSettingEnabled(boolean enabled)
boolean isWiFiRoamingSettingEnabled()
Benzer şekilde, diğer davranışlara veya özelliklere bağımlılığı belirten yöntemler is ön ekiyle Supported (Destekleniyor) veya Required (Zorunlu) son ekiyle kullanılabilir:
// "Supported" describes whether this API would work on devices that support
// multiple users. The API "supports" multi-user:
void setMultiUserSupported(boolean supported)
boolean isMultiUserSupported()
// "Required" describes whether this API depends on devices that support
// multiple users. The API "requires" multi-user:
void setMultiUserRequired(boolean required)
boolean isMultiUserRequired()
Genellikle yöntem adları, döndürülen değer tarafından yanıtlanan sorular olarak yazılmalıdır.
Kotlin mülk yöntemleri
var foo: Foo
sınıf mülkü için Kotlin, tutarlı bir kural kullanarak get
/set
yöntemleri oluşturur: Geter için get
önek eklenir ve ilk karakter büyük yazılır, ayarlayıcı için set
önek eklenir ve ilk karakter büyük yazılır. Mülk beyanı sırasıyla public Foo getFoo()
ve public void setFoo(Foo foo)
adlı yöntemler oluşturur.
Mülk Boolean
türüne sahipse ad oluşturma işleminde ek bir kural geçerlidir: Mülk adı is
ile başlıyorsa alıcı yöntem adı için get
öne eklenmez, alıcı olarak mülkün adı kullanılır.
Bu nedenle, adlandırma kuralına uymak için Boolean
mülklerini is
ön ekiyle adlandırmayı tercih edin:
var isVisible: Boolean
Mülkünüz yukarıda belirtilen istisnalardan biriyse ve uygun bir önek ile başlıyorsa uygun adı manuel olarak belirtmek için mülkte @get:JvmName
ek açıklamasını kullanın:
@get:JvmName("hasTransientState")
var hasTransientState: Boolean
@get:JvmName("canRecord")
var canRecord: Boolean
@get:JvmName("shouldFitWidth")
var shouldFitWidth: Boolean
Bit maskesi erişim araçları
Bit maskesi işaretlerini tanımlamayla ilgili API kuralları için Bit maskesi işaretleri için @IntDef
kullanın başlıklı makaleyi inceleyin.
Ayarlayıcılar
İki ayarlama yöntemi sağlanmalıdır: Biri tam bir bit dizesi alan ve mevcut tüm işaretlerin üzerine yazan, diğeri ise daha fazla esneklik sağlamak için özel bir bit maskesi alan.
/**
* Sets the state of all scroll indicators.
* <p>
* See {@link #setScrollIndicators(int, int)} for usage information.
*
* @param indicators a bitmask of indicators that should be enabled, or
* {@code 0} to disable all indicators
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public void setScrollIndicators(@ScrollIndicators int indicators);
/**
* Sets the state of the scroll indicators specified by the mask. To change
* all scroll indicators at once, see {@link #setScrollIndicators(int)}.
* <p>
* When a scroll indicator is enabled, it will be displayed if the view
* can scroll in the direction of the indicator.
* <p>
* Multiple indicator types may be enabled or disabled by passing the
* logical OR of the specified types. If multiple types are specified, they
* will all be set to the same enabled state.
* <p>
* For example, to enable the top scroll indicator:
* {@code setScrollIndicators(SCROLL_INDICATOR_TOP, SCROLL_INDICATOR_TOP)}
* <p>
* To disable the top scroll indicator:
* {@code setScrollIndicators(0, SCROLL_INDICATOR_TOP)}
*
* @param indicators a bitmask of values to set; may be a single flag,
* the logical OR of multiple flags, or 0 to clear
* @param mask a bitmask indicating which indicator flags to modify
* @see #setScrollIndicators(int)
* @see #getScrollIndicators()
*/
public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask);
Alıcı
Tam bit maskesini almak için bir alıcı sağlanmalıdır.
/**
* Returns a bitmask representing the enabled scroll indicators.
* <p>
* For example, if the top and left scroll indicators are enabled and all
* other indicators are disabled, the return value will be
* {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}.
* <p>
* To check whether the bottom scroll indicator is enabled, use the value
* of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}.
*
* @return a bitmask representing the enabled scroll indicators
*/
@ScrollIndicators
public int getScrollIndicators();
Koruma altında yerine herkese açık kullanın
Her zaman herkese açık API'de public
yerine protected
'ü tercih edin. Uygulamacıların, varsayılan olarak harici erişimin de aynı derecede iyi olacağı durumlarda herkese açık erişim sağlayıcılar sağlamak için geçersiz kılma işlemi yapması gerektiğinden, korumalı erişim uzun vadede can sıkıcı olabilir.
protected
Görünürlük, geliştiricilerin API çağırmasını engellemez. Yalnızca bu işlemi biraz daha can sıkıcı hale getirir.
equals() ve hashCode() yöntemlerinin hiçbirini veya ikisini birden uygulama
Birini geçersiz kılarsanız diğerini de geçersiz kılmanız gerekir.
Veri sınıfları için toString() işlevini uygulama
Veri sınıflarının, geliştiricilerin kodlarında hata ayıklamalarına yardımcı olmak için toString()
değerini geçersiz kılması önerilir.
Çıktının program davranışı için mi yoksa hata ayıklama için mi olduğunu belirtin.
Program davranışının uygulamanıza bağlı olmasını isteyip istemediğinize karar verin. Örneğin, UUID.toString() ve File.toString(), programların kullanacağı özel biçimlerini belgeler. Yalnızca hata ayıklama için bilgi (ör. Intent) sunuyorsanız dokümanları üst sınıftan devralın.
Ek bilgi eklemeyin
toString()
'ten edinilebilen tüm bilgiler, nesnenin herkese açık API'si aracılığıyla da edinilebilmelidir. Aksi takdirde, geliştiricileri toString()
çıkışınızı ayrıştırmaya ve kullanmaya teşvik edersiniz. Bu da gelecekteki değişiklikleri engeller. toString()
'ü yalnızca nesnenin herkese açık API'sini kullanarak uygulamak iyi bir uygulamadır.
Hata ayıklama çıkışına güvenmeyin
Geliştiricilerin hata ayıklama çıkışına güvenmesini önlemek mümkün olmasa da, nesnenizin toString()
çıkışına System.identityHashCode
dahil edilmesi, iki farklı nesnenin eşit toString()
çıkışına sahip olma olasılığını çok düşük hale getirir.
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " {mFoo=" + mFoo + "}";
}
Bu durum, geliştiricilerin nesnelerinizde assertThat(a.toString()).isEqualTo(b.toString())
gibi test ifadeleri yazmasını etkili bir şekilde engelleyebilir.
Yeni oluşturulan nesneleri döndürmek için createFoo'yu kullanın
Örneğin, yeni nesneler oluşturarak döndürülen değerler oluşturacak yöntemler için get
veya new
yerine create
ön ekini kullanın.
Yöntem, döndürülecek bir nesne oluşturacaksa bunu yöntem adında açıkça belirtin.
public FooThing getFooThing() {
return new FooThing();
}
public FooThing createFooThing() {
return new FooThing();
}
Dosya nesnelerini kabul eden yöntemler, akışları da kabul etmelidir.
Android'deki veri depolama konumları her zaman diskteki dosyalar değildir. Örneğin, kullanıcı sınırları arasında aktarılan içerikler content://
Uri
olarak gösterilir. Çeşitli veri kaynaklarının işlenmesini sağlamak için File
nesnelerini kabul eden API'ler InputStream
, OutputStream
veya her ikisini de kabul etmelidir.
public void setDataSource(File file)
public void setDataSource(InputStream stream)
Kutudaki sürümler yerine ham primitifleri alıp iade etme
Eksik veya boş değerleri iletmeniz gerekiyorsa -1
, Integer.MAX_VALUE
veya Integer.MIN_VALUE
kullanmayı düşünebilirsiniz.
public java.lang.Integer getLength()
public void setLength(java.lang.Integer)
public int getLength()
public void setLength(int value)
Basit türlerin sınıf eşdeğerlerinden kaçınmak, bu sınıfların bellek yükü, değerlere yöntem erişimi ve daha da önemlisi, basit ve nesne türleri arasında yayınlamadan kaynaklanan otomatik kutuyu önler. Bu davranışlardan kaçınmak, pahalı ve daha sık çöp toplama işlemlerine yol açabilecek bellek ve geçici atamalardan tasarruf sağlar.
Geçerli parametre ve döndürülen değerleri netleştirmek için ek açıklamaları kullanma
Çeşitli durumlarda izin verilen değerlerin netleştirilmesine yardımcı olmak için geliştirici ek açıklamaları eklendi. Bu sayede, geliştiriciler yanlış değerler sağladığında (örneğin, çerçeve belirli bir sabit değer grubundan birini gerektirirken rastgele bir int
gönderdiğinde) araçların geliştiricilere yardımcı olması kolaylaşır. Aşağıdaki ek açıklamaların tümünü uygun durumlarda kullanın:
Boş değer atanabilirliği
Java API'leri için açık null değer alabilirlik ek açıklamaları gereklidir. Ancak null değer alabilirlik kavramı Kotlin dilinin bir parçasıdır ve null değer alabilirlik ek açıklamaları Kotlin API'lerinde hiçbir zaman kullanılmamalıdır.
@Nullable
: Belirli bir döndürülen değerin, parametrenin veya alanın null olabileceğini belirtir:
@Nullable
public String getName()
public void setName(@Nullable String name)
@NonNull
: Belirli bir döndürülen değerin, parametrenin veya alanın boş olamayacağı anlamına gelir. Öğeleri @Nullable
olarak işaretlemek Android'de nispeten yeni bir özellik olduğundan Android API yöntemlerinin çoğu tutarlı bir şekilde belgelenmemiştir. Bu nedenle, "bilinmiyor, @Nullable
, @NonNull
" üçlü durumuna sahibiz. Bu nedenle @NonNull
, API yönergelerinin bir parçasıdır:
@NonNull
public String getName()
public void setName(@NonNull String name)
Android platform dokümanlarındaki yöntem parametrelerinize not eklediğinizde, "null" parametre dokümanının başka bir yerinde açıkça kullanılmadığı sürece "Bu değer null olabilir." şeklinde otomatik olarak doküman oluşturulur.
Mevcut "gerçekten boş olmayan" yöntemler: API'de, tanımlanmış bir @Nullable
ek açıklaması olmayan mevcut yöntemler, belirli ve açık koşullar altında (findViewById()
gibi) null
döndürebiliyorsa @Nullable
ek açıklamasıyla eklenebilir. Boş kontrol etmek istemeyen geliştiriciler için IllegalArgumentException
atanacak tamamlayıcı @NotNull requireFoo()
yöntemleri eklenmelidir.
Arayüz yöntemleri: Yeni API'ler, Parcelable.writeToParcel()
gibi arayüz yöntemlerini uygularken uygun ek açıklamayı eklemelidir (yani, uygulayıcı sınıftaki bu yöntem writeToParcel(Parcel, int)
değil writeToParcel(@NonNull Parcel,
int)
olmalıdır); ancak ek açıklamaları eksik olan mevcut API'lerin "düzeltilmesi" gerekmez.
Boş değer atanabilirliği yaptırımı
Java'da, @NonNull
parametreleri için giriş doğrulaması yapmak amacıyla Objects.requireNonNull()
yönteminin kullanılması ve parametreler null olduğunda NullPointerException
atılması önerilir. Bu işlem Kotlin'de otomatik olarak gerçekleştirilir.
Kaynaklar
Kaynak tanımlayıcıları: Belirli kaynakların kimliklerini belirten tam sayı parametreleri, uygun kaynak türü tanımı ile ek açıklamaya tabi tutulmalıdır.
Her kaynak türü için @AnyRes
gibi bir genel notun yanı sıra @StringRes
,
@ColorRes
ve @AnimRes
gibi notlar vardır. Örneğin:
public void setTitle(@StringRes int resId)
Sabit kümeler için @IntDef
Sihirli sabitler: Herkese açık sabitlerle gösterilen sonlu bir olası değer grubundan birini alması amaçlanan String
ve int
parametreleri, @StringDef
veya @IntDef
ile uygun şekilde ek açıklamaya tabi tutulmalıdır. Bu ek açıklamalar, izin verilen parametreler için typedef gibi çalışan, kullanabileceğiniz yeni bir ek açıklama oluşturmanıza olanak tanır. Örneğin:
/** @hide */
@IntDef(prefix = {"NAVIGATION_MODE_"}, value = {
NAVIGATION_MODE_STANDARD,
NAVIGATION_MODE_LIST,
NAVIGATION_MODE_TABS
})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
@NavigationMode
public int getNavigationMode();
public void setNavigationMode(@NavigationMode int mode);
Notlandırılmış parametrelerin geçerliliğini kontrol etmek ve parametre @IntDef
kapsamında değilse IllegalArgumentException
oluşturmak için yöntemler önerilir.
Bit maskesi işaretleri için @IntDef
Ek açıklama, sabitlerin işaret olduğunu ve & ile I ile birleştirilebileceğini de belirtebilir:
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_USE_LOGO,
FLAG_SHOW_HOME,
FLAG_HOME_AS_UP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}
Dize sabit kümeleri için @StringDef
Ayrıca, önceki bölümdeki @IntDef
ile aynı olan ancak String
sabitleri için olan @StringDef
ek açıklaması da vardır. Tüm değerler için dokümanları otomatik olarak yayınlamak üzere kullanılan birden fazla "önek" değeri ekleyebilirsiniz.
SDK sabitleri için @SdkConstant
@SdkConstant ACTIVITY_INTENT_ACTION
, BROADCAST_INTENT_ACTION
, SERVICE_ACTION
, INTENT_CATEGORY
, FEATURE
değerlerinden biri olan herkese açık alanlara ek açıklama ekleyin.SdkConstant
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CALL = "android.intent.action.CALL";
Geçersiz kılmalar için uyumlu boşluk özelliği sağlayın
API uyumluluğu için geçersiz kılma işlemlerinin boşluk kabul edebileceği durumlar, üst öğenin mevcut boşluk kabul edebileceği durumlarla uyumlu olmalıdır. Aşağıdaki tabloda uyumluluk beklentileri gösterilmektedir. Açıkça belirtmek gerekirse, geçersiz kılma işlemleri yalnızca geçersiz kıldığı öğe kadar veya bu öğeden daha kısıtlayıcı olmalıdır.
Tür | Ebeveyn | Çocuğum için |
---|---|---|
Dönüş türü | Not eklenmemiş | Açıklama içermeyen veya boş olmayan |
Dönüş türü | Boş değer atanabilir | Boş bırakılabilir veya boş bırakılamaz |
Dönüş türü | Nonnull | Nonnull |
Eğlenceli bağımsız değişken | Not eklenmemiş | Açıklama içermeyen veya boş değer alabilir |
Eğlenceli bağımsız değişken | Boş değer atanabilir | Boş değer atanabilir |
Eğlenceli bağımsız değişken | Nonnull | Boş bırakılabilir veya boş bırakılamaz |
Mümkün olduğunda boş olmayan bağımsız değişkenleri (ör. @NonNull) tercih edin
Yöntemler aşırı yüklendiğinde tüm bağımsız değişkenlerin boş olmaması tercih edilir.
public void startActivity(@NonNull Component component) { ... }
public void startActivity(@NonNull Component component, @NonNull Bundle options) { ... }
Bu kural, aşırı yüklenmiş mülk ayarlayıcıları için de geçerlidir. Birincil bağımsız değişken null olmamalıdır ve mülkün temizlenmesi ayrı bir yöntem olarak uygulanmalıdır. Bu, geliştiricinin zorunlu olmasa bile son parametreleri ayarlamasının gerektiği "saçma" çağrıları önler.
public void setTitleItem(@Nullable IconCompat icon, @ImageMode mode)
public void setTitleItem(@Nullable IconCompat icon, @ImageMode mode, boolean isLoading)
// Nonsense call to clear property
setTitleItem(null, MODE_RAW, false);
public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode)
public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode, boolean isLoading)
public void clearTitleItem()
Kapsayıcılarda null olmayan (ör. @NonNull) döndürme türlerini tercih edin
Bundle
veya Collection
gibi kapsayıcı türleri için boş (ve uygun durumlarda değiştirilemez) bir kapsayıcı döndürün. Bir kapsayıcının kullanılabilirliğini ayırt etmek için null
'ün kullanılacağı durumlarda ayrı bir boole yöntemi sağlamayı düşünün.
@NonNull
public Bundle getExtras() { ... }
Get ve set çiftleri için geçersizlik ek açıklamaları eşleşmelidir
Tek bir mantıksal mülk için get ve set yöntemi çiftleri, boşluk kabul edilebilirlik ek açıklamalarında her zaman aynı olmalıdır. Bu kurala uyulmaması, Kotlin'in mülk söz dizimini geçersiz kılar. Bu nedenle, mevcut mülk yöntemlerine tutarsız boşluk kabul edilebilirlik ek açıklamaları eklemek, Kotlin kullanıcıları için kaynakta değişiklik gerektiren bir değişikliktir.
@NonNull
public Bundle getExtras() { ... }
public void setExtras(@NonNull Bundle bundle) { ... }
Başarısızlık veya hata koşullarında değer döndürme
Tüm API'ler, uygulamaların hatalara tepki vermesine izin vermelidir. false
, -1
, null
veya "bir şeyler ters gitti" gibi diğer genel değerleri döndürmek, geliştiricinin kullanıcı beklentilerini belirleme veya uygulamalarının sahadaki güvenilirliğini doğru şekilde izleme konusunda başarısız olduğu konusunda yeterli bilgi sağlamaz. API tasarlarken bir uygulama oluşturduğunuzu düşünün. Bir hatayla karşılaşırsanız API, hatayı kullanıcıya sunmak veya uygun şekilde tepki vermek için size yeterli bilgi veriyor mu?
- İstisnalar mesajına ayrıntılı bilgi eklemek sorun oluşturmaz (ve önerilir) ancak geliştiricilerin hatayı uygun şekilde ele almak için bu bilgileri ayrıştırması gerekmemelidir. Ayrıntılı hata kodları veya diğer bilgiler yöntem olarak gösterilmelidir.
- Seçtiğiniz hata işleme seçeneğinin, gelecekte yeni hata türleri ekleme esnekliği sağladığından emin olun.
@IntDef
için bu, birOTHER
veyaUNKNOWN
değeri eklemek anlamına gelir. Yeni bir kod döndürdüğünüzde, uygulamanın bilmediği bir hata kodu döndürmemek için arayanıntargetSdkVersion
değerini kontrol edebilirsiniz. İstisnalar için, istisnalarınızın uyguladığı ortak bir üst sınıf kullanın. Böylece, bu türü işleyen tüm kodlar alt türleri de yakalayıp işleyebilir. - Bir geliştiricinin yanlışlıkla hatayı yoksayması zor veya imkansız olmalıdır. Hatanızın değeri döndürerek iletildiği durumlarda yönteminizi
@CheckResult
ile notlandırın.
Geliştiricinin yanlış yaptığı bir şey (ör. giriş parametrelerindeki kısıtlamaları göz ardı etme veya gözlemlenebilir durumu kontrol etmeme) nedeniyle bir hata veya başarısızlık durumuna ulaşıldığında ? extends RuntimeException
atmayı tercih edin.
Ayarlayıcı veya işlem (örneğin, perform
) yöntemleri, işlem ayarsız bir şekilde güncellenen durum veya geliştiricinin kontrolü dışındaki koşullar nedeniyle başarısız olabilirse tam sayı durum kodu döndürebilir.
Durum kodları, içeren sınıfta public static final
alanı olarak tanımlanmalı, ERROR_
önekiyle başlamalı ve @hide
@IntDef
ek açıklamalarında listelenmelidir.
Yöntem adları her zaman özneyle değil, fiille başlamalıdır.
Yöntemin adı, her zaman üzerinde işlem yaptığınız nesneyle değil, fiille (ör. get
, create
, reload
vb.) başlamalıdır.
public void tableReload() {
mTable.reload();
}
public void reloadTable() {
mTable.reload();
}
Dönüş veya parametre türü olarak koleksiyon türlerini dizilere tercih edin
Genel olarak yazılmış koleksiyon arayüzleri, benzersizlik ve sıralama ile ilgili daha güçlü API sözleşmeleri, geneller için destek ve geliştirici dostu çeşitli kolaylık yöntemleri de dahil olmak üzere dizilere göre çeşitli avantajlar sunar.
Temel öğeler için istisna
Öğeler ilkel ise otomatik kutuya yerleştirme maliyetini önlemek için bunun yerine dizileri tercih edin. Paketlenmiş sürümler yerine ham primitifleri alma ve döndürme başlıklı makaleyi inceleyin.
Performansa duyarlı kod için istisna
API'nin performansa duyarlı kodda (grafikler veya diğer ölçüm/düzen/çizim API'leri gibi) kullanıldığı belirli senaryolarda, tahsisleri ve bellek değişimini azaltmak için koleksiyonlar yerine diziler kullanılması kabul edilebilir.
Kotlin için istisna
Kotlin dizileri değişmezdir ve Kotlin dili, diziler için yeterli sayıda yardımcı API sağlar. Bu nedenle, Kotlin'den erişilmesi amaçlanan Kotlin API'leri için diziler List
ve Collection
ile aynı düzeydedir.
@NonNull koleksiyonları tercih edin
Koleksiyon nesneleri için her zaman @NonNull
değerini tercih edin. Boş bir koleksiyon döndürürken uygun Collections.empty
yöntemini kullanarak düşük maliyetli, doğru yazılmış ve değiştirilemeyen bir koleksiyon nesnesi döndürün.
Tür ek açıklamalarının desteklenmediği durumlarda, koleksiyon öğeleri için her zaman @NonNull
değerini tercih edin.
Koleksiyonlar yerine diziler kullanırken de @NonNull
değerini tercih etmeniz gerekir (önceki öğeye bakın). Nesne tahsisi konusunda endişeleriniz varsa bir sabit oluşturun ve iletin. Sonuçta boş bir dizi değişmezdir. Örnek:
private static final int[] EMPTY_USER_IDS = new int[0];
@NonNull
public int[] getUserIds() {
int [] userIds = mService.getUserIds();
return userIds != null ? userIds : EMPTY_USER_IDS;
}
Koleksiyonların değiştirilebilirliği
Kotlin API'leri, API sözleşmesi özellikle değiştirilebilir bir döndürülen tür gerektirmediği sürece varsayılan olarak koleksiyonlar için salt okunur (Mutable
değil) döndürülen türleri tercih etmelidir.
Ancak Java API'lerinin Android platform uygulaması henüz değişmez koleksiyonların uygun bir uygulamasını sağlamadığından Java API'leri varsayılan olarak değişken dönüş türlerini tercih etmelidir. Bu kuralın istisnası, değiştirilemeyen Collections.empty
döndürme türleridir. Değişkenliğin, API'nin amaçlanan kullanım şeklini bozmak için istemciler tarafından kasıtlı veya yanlışlıkla kötüye kullanılabileceği durumlarda Java API'lerinin koleksiyonun yüzeysel bir kopyasını döndürmesi önemle tavsiye edilir.
@Nullable
public PermissionInfo[] getGrantedPermissions() {
return mPermissions;
}
@NonNull
public Set<PermissionInfo> getGrantedPermissions() {
if (mPermissions == null) {
return Collections.emptySet();
}
return new ArraySet<>(mPermissions);
}
Açıkça değişken iade türleri
Koleksiyon döndüren API'ler, döndürülen koleksiyon nesnesini ideal olarak döndürdükten sonra değiştirmemelidir. Döndürülen koleksiyonun değişmesi veya bir şekilde yeniden kullanılması gerekiyorsa (ör. değiştirilebilir bir veri kümesinin uyarlanmış görünümü) içeriğin ne zaman değişebileceğine dair kesin davranış açıkça belgelenmeli veya belirlenmiş API adlandırma kurallarına uymalıdır.
/**
* Returns a view of this object as a list of [Item]s.
*/
fun MyObject.asList(): List<Item> = MyObjectListWrapper(this)
Kotlin .asFoo()
kuralı aşağıda açıklanmıştır ve orijinal koleksiyon değişirse .asList()
tarafından döndürülen koleksiyonun değişmesine izin verir.
Döndürülen veri türü nesnelerinin değişebilirliği
Koleksiyon döndüren API'lere benzer şekilde, veri türündeki nesneleri döndüren API'ler de ideal olarak döndürülen nesnenin özelliklerini değiştirmemelidir.
val tempResult = DataContainer()
fun add(other: DataContainer): DataContainer {
tempResult.innerValue = innerValue + other.innerValue
return tempResult
}
fun add(other: DataContainer): DataContainer {
return DataContainer(innerValue + other.innerValue)
}
Son derece sınırlı durumlarda, performansa duyarlı bazı kodlar nesne havuzundan veya yeniden kullanımdan yararlanabilir. Kendi nesne havuzu veri yapınızı yazmayın ve yeniden kullanılan nesneleri herkese açık API'lerde göstermeyin. Her iki durumda da eşzamanlı erişimi yönetme konusunda son derece dikkatli olun.
vararg parametre türünün kullanımı
Geliştiricinin, yalnızca aynı türde birden fazla ilgili parametre iletmek amacıyla çağrı yerinde bir dizi oluşturma olasılığının olduğu durumlarda hem Kotlin hem de Java API'lerinin vararg
kullanması önerilir.
public void setFeatures(Feature[] features) { ... }
// Developer code
setFeatures(new Feature[]{Features.A, Features.B, Features.C});
public void setFeatures(Feature... features) { ... }
// Developer code
setFeatures(Features.A, Features.B, Features.C);
Koruyucu kopyalar
vararg
parametrelerinin hem Java hem de Kotlin uygulamaları aynı dizi destekli bayt koduna derlenir ve sonuç olarak Java kodundan, değiştirilebilir bir dizi ile çağrılabilir. API tasarımcılarının, bir alanda veya anonim iç sınıfta kalıcı hale getirilecek durumlarda dizi parametresinin savunma amaçlı bir sığ kopyasını oluşturmaları önemle tavsiye edilir.
public void setValues(SomeObject... values) {
this.values = Arrays.copyOf(values, values.length);
}
Koruyucu bir kopya oluşturmanın, ilk yöntem çağrısı ile kopyanın oluşturulması arasında eşzamanlı değişikliğe karşı herhangi bir koruma sağlamadığını ve dizideki nesnelerin mutasyonuna karşı da koruma sağlamadığını unutmayın.
Koleksiyon türü parametreleri veya döndürülen türlerle doğru anlamlar sağlayın
List<Foo>
varsayılan seçenektir ancak ek anlam sağlamak için diğer türleri de kullanabilirsiniz:
API'niz öğelerin sırasına önem vermiyorsa, kopyalara izin vermiyorsa veya kopyalar anlamsızsa
Set<Foo>
değerini kullanın.API'niz sıraya bakmazsa ve yinelenen öğelere izin veriyorsa
Collection<Foo>,
.
Kotlin dönüşüm işlevleri
Kotlin, mevcut bir nesneden farklı türde bir nesne elde etmek için sık sık .toFoo()
ve .asFoo()
kullanır. Bu durumda Foo
, dönüşümün döndürdüğü türün adıdır. Bu, tanıdık JDK ile tutarlıdırObject.toString()
. Kotlin, bu özelliği 25.toFloat()
gibi ilkel dönüşümler için kullanarak daha da ileri götürür.
.toFoo()
ve .asFoo()
adlı dönüşümler arasındaki fark önemlidir:
Yeni ve bağımsız bir nesne oluştururken .toFoo() işlevini kullanma
.toString()
gibi "to" dönüşümü de yeni ve bağımsız bir nesne döndürür. Orijinal nesne daha sonra değiştirilirse yeni nesne bu değişiklikleri yansıtmaz.
Benzer şekilde, yeni nesne daha sonra değiştirilirse eski nesne bu değişiklikleri yansıtmaz.
fun Foo.toBundle(): Bundle = Bundle().apply {
putInt(FOO_VALUE_KEY, value)
}
Bağımlı sarmalayıcı, süslenmiş nesne veya cast oluştururken .asFoo() işlevini kullanın
Kotlin'de yayınlama işlemi as
anahtar kelimesi kullanılarak gerçekleştirilir. Bu, arayüzde yapılan bir değişikliği yansıtır ancak kimlikte yapılan bir değişikliği yansıtmaz. .asFoo()
, bir uzantı işlevinde önek olarak kullanıldığında alıcıyı süsler. Orijinal alıcı nesnesinde yapılan bir mutasyon, asFoo()
tarafından döndürülen nesneye yansıtılır.
Yeni Foo
nesnesinde yapılan bir mutasyon, orijinal nesneye yansıtılabilir.
fun <T> Flow<T>.asLiveData(): LiveData<T> = liveData {
collect {
emit(it)
}
}
Dönüşüm işlevleri, uzantı işlevi olarak yazılmalıdır
Dönüşüm işlevlerini hem alıcı hem de sonuç sınıfı tanımlarının dışında yazmak, türler arasındaki bağlantıyı azaltır. İdeal bir dönüşüm için yalnızca orijinal nesneye herkese açık API erişimi gerekir. Bu örnek, geliştiricilerin kendi tercih ettikleri türlere benzer dönüşümler de yazabileceğini gösterir.
Uygun özel istisnalar gönderme
Yöntemler java.lang.Exception
veya java.lang.Throwable
gibi genel istisnalar atmamalıdır. Bunun yerine, geliştiricilerin istisnaları çok geniş kapsamlı olmadan ele almasına olanak tanımak için java.lang.NullPointerException
gibi uygun bir istisna kullanılmalıdır.
Doğrudan herkese açık olarak çağrılan yönteme sağlanan bağımsız değişkenlerle alakasız hatalar, java.lang.IllegalArgumentException
veya java.lang.NullPointerException
yerine java.lang.IllegalStateException
hatası atmalıdır.
Dinleyiciler ve geri aramalar
Bunlar, dinleyici ve geri çağırma mekanizmaları için kullanılan sınıflar ve yöntemlerle ilgili kurallardır.
Geri çağırma sınıfı adları tekil olmalıdır
MyObjectCallbacks
yerine MyObjectCallback
kullanın.
Geri çağırma yöntemi adları on biçiminde olmalıdır.
onFooEvent
, FooEvent
işleminin gerçekleştiğini ve geri çağırma işlevinin buna göre hareket etmesi gerektiğini belirtir.
Geçmiş ve şimdiki zaman, zamanlama davranışını tanımlamalıdır
Etkinliklerle ilgili geri arama yöntemleri, etkinliğin gerçekleşip gerçekleşmediğini veya gerçekleşme sürecinde olup olmadığını belirtecek şekilde adlandırılmalıdır.
Örneğin, bir tıklama işlemi gerçekleştirildikten sonra yöntem çağrılırsa:
public void onClicked()
Ancak yöntem, tıklama işlemini gerçekleştirmekten sorumluysa:
public boolean onClick()
Geri arama kaydı
Bir dinleyici veya geri çağırma işlevi bir nesneye eklenip kaldırılabiliyorsa ilişkili yöntemler add ve remove veya register ve unregister olarak adlandırılmalıdır. Sınıf tarafından veya aynı paketteki diğer sınıflar tarafından kullanılan mevcut kurallara uygun olmalıdır. Böyle bir örnek yoksa ekleme ve kaldırmayı tercih edin.
Geri çağırmaların kaydedilmesini veya geri çağırmaların kaydının silinmesini içeren yöntemler, geri çağırma türünün tam adını belirtmelidir.
public void addFooCallback(@NonNull FooCallback callback);
public void removeFooCallback(@NonNull FooCallback callback);
public void registerFooCallback(@NonNull FooCallback callback);
public void unregisterFooCallback(@NonNull FooCallback callback);
Geri çağırma işlevleri için alıcılardan kaçının
getFooCallback()
yöntemleri eklemeyin. Bu, geliştiricilerin mevcut bir geri çağırmayı kendi yerine geçecek bir geri çağırmaya zincirlemek isteyebileceği durumlarda cazip bir kaçış yoludur ancak kararsızdır ve bileşen geliştiricilerinin mevcut durumu anlamasını zorlaştırır. Örneğin,
- Geliştirici A,
setFooCallback(a)
numaralı telefonu arar. - Geliştirici B,
setFooCallback(new B(getFooCallback()))
'yi arar - A geliştiricisi,
a
geri çağırma işlevini kaldırmak istiyor ancakB
türü hakkında bilgi sahibi olmadığı veB
, sarmalanmış geri çağırma işlevinde bu tür değişikliklere izin verecek şekilde tasarlanmadığı için bunu yapamıyor.
Geri arama gönderimini kontrol etmek için Executor'ı kabul etme
Belirli bir mesaj dizisi beklentisi olmayan geri çağırma işlevlerini (kullanıcı arayüzü araç setinin dışındaki hemen her yerde) kaydederken, geliştiricinin geri çağırma işlevlerinin çağrılacağı mesaj dizisini belirtmesine olanak tanımak için kayıt işlemine bir Executor
parametresi eklemeniz önemle tavsiye edilir.
public void registerFooCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull FooCallback callback)
İsteğe bağlı parametrelerle ilgili genel kurallarımıza istisna olarak, parametre listesindeki son bağımsız değişken olmasa bile Executor
parametresini atlayan bir aşırı yükleme sağlamak kabul edilebilir. Executor
sağlanmazsa geri çağırma işlevi, Looper.getMainLooper()
kullanılarak ana mesaj dizisinde çağrılmalıdır ve bu, ilişkili aşırı yüklenen yöntemde belirtilmelidir.
/**
* ...
* Note that the callback will be executed on the main thread using
* {@link Looper.getMainLooper()}. To specify the execution thread, use
* {@link registerFooCallback(Executor, FooCallback)}.
* ...
*/
public void registerFooCallback(
@NonNull FooCallback callback)
public void registerFooCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull FooCallback callback)
Executor
uygulamayla ilgili dikkat edilmesi gereken noktalar: Aşağıdakilerin geçerli bir yürütücü olduğunu unutmayın.
public class SynchronousExecutor implements Executor {
@Override
public void execute(Runnable r) {
r.run();
}
}
Bu, bu formu kullanan API'leri uygularken uygulama işlemi tarafındaki gelen bağlayıcı nesnesi uygulamanızın, uygulama tarafından sağlanan Executor
üzerinde uygulamanın geri çağırma işlevini çağırmadan önce Binder.clearCallingIdentity()
'yi çağırması gerektiği anlamına gelir. Bu sayede, izin kontrolleri için bağlayıcı kimliğini (Binder.getCallingUid()
gibi) kullanan tüm uygulama kodları, çalışan kodu uygulamayı çağıran sistem işlemine değil, uygulamaya doğru şekilde ilişkilendirir. API'nizin kullanıcıları arayan kullanıcının UID veya PID bilgilerini istiyorsa bu, sağladıkları Executor
'ün çalıştırıldığı yere bağlı olarak dolaylı değil, API yüzeyinizin açık bir parçası olmalıdır.
Executor
belirtmek API'niz tarafından desteklenmelidir. Performans açısından kritik durumlarda uygulamaların kodu hemen veya API'nizden gelen geri bildirimle senkronize olarak çalıştırması gerekebilir. Executor
kabul etmek buna izin verir.
Trampoline'den savunma amaçlı olarak ek bir HandlerThread
veya benzer bir öğe oluşturmak, bu istenilen kullanım alanını geçersiz kılar.
Bir uygulama kendi işleminde pahalı kod çalıştıracaksa buna izin verin. Uygulama geliştiricilerin kısıtlamalarınızı aşmak için bulacağı geçici çözümlerin uzun vadede desteklenmesi çok daha zor olacaktır.
Tek geri çağırma istisnası: Bildirilen etkinliklerin doğası gereği yalnızca tek bir geri çağırma örneğinin desteklenmesi gerektiğinde aşağıdaki stili kullanın:
public void setFooCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull FooCallback callback)
public void clearFooCallback()
İşleyici yerine yürütücü kullanın
Android'in Handler
, geçmişte geri çağırma yürütme işlemini belirli bir Looper
iş parçacığına yönlendirmek için standart olarak kullanılıyordu. Çoğu uygulama geliştiricisi kendi iş parçacığı havuzlarını yönettiğinden bu standart, Executor
'ü tercih edecek şekilde değiştirildi. Bu da ana veya kullanıcı arayüzü iş parçacığının, uygulamanın kullanabileceği tek Looper
iş parçası olmasını sağlar. Geliştiricilere mevcut/tercih edilen yürütme bağlamlarını yeniden kullanmak için ihtiyaç duydukları kontrolü sağlamak üzere Executor
kullanın.
kotlinx.coroutines veya RxJava gibi modern eşzamanlılık kitaplıkları, gerektiğinde kendi dağıtımlarını gerçekleştiren kendi planlama mekanizmalarını sağlar. Bu nedenle, çift iş parçacığı atlamalarından kaynaklanan gecikmeleri önlemek için doğrudan bir yürütücü (Runnable::run
gibi) kullanma olanağı sunmak önemlidir. Örneğin, Handler
kullanarak bir Looper
mesaj dizisine göndermek için bir durak, ardından uygulamanın eşzamanlılık çerçevesinden başka bir durak.
Bu kuralın istisnaları nadirdir. İstisna taleplerinde sık karşılaşılan durumlar şunlardır:
Etkinlik için epoll
yapmak üzere Looper
'a ihtiyacım olduğu için Looper
kullanmam gerekiyor.
Bu durumda Executor
'ün avantajlarından yararlanılamayacağı için bu istisna isteği kabul edildi.
Uygulama kodunun, etkinliği yayınlayan ileti dizisini engellemesini istemiyorum. Bu istisna isteği, genellikle bir uygulama sürecinde çalışan kod için verilmez. Bu konuda yanlış yapan uygulamalar yalnızca kendilerine zarar verir, genel sistem sağlığını etkilemez. Bu konuda doğru adımları atan veya ortak bir eşzamanlılık çerçevesi kullanan uygulamalar ek gecikme cezaları ödememelidir.
Handler
, aynı sınıftaki diğer benzer API'lerle yerel olarak tutarlıdır.
Bu istisna isteği durumsal olarak kabul edilir. Tercih, Executor
tabanlı aşırı yüklemelerin eklenmesi ve Handler
uygulamalarının yeni Executor
uygulamasını kullanacak şekilde taşınmasıdır. (myHandler::post
geçerli bir Executor
'tır!) Sınıfın boyutuna, mevcut Handler
yöntemlerinin sayısına ve geliştiricilerin yeni yöntemle birlikte mevcut Handler
tabanlı yöntemleri kullanması gerekme olasılığına bağlı olarak, yeni bir Handler
tabanlı yöntem eklemek için istisna verilebilir.
Kayıttaki simetri
Bir öğeyi eklemenin veya kaydettirmenin bir yolu varsa bu öğeyi kaldırmanın/kaydını iptal etmenin de bir yolu olmalıdır. Yöntem
registerThing(Thing)
eşleşen bir
unregisterThing(Thing)
İstek tanımlayıcısı sağlama
Geliştiricinin geri çağırmayı yeniden kullanması makulse geri çağırmayı isteğe bağlamak için bir tanımlayıcı nesnesi sağlayın.
class RequestParameters {
public int getId() { ... }
}
class RequestExecutor {
public void executeRequest(
RequestParameters parameters,
Consumer<RequestParameters> onRequestCompletedListener) { ... }
}
Birden fazla yöntem içeren geri çağırma nesneleri
Birden fazla yöntem içeren geri çağırma işlevleri, daha önce yayınlanan arayüzlere eklenirken interface
yöntemini tercih etmeli ve default
yöntemlerini kullanmalıdır. Önceden bu kural, Java 7'de default
yöntemlerinin bulunmaması nedeniyle abstract class
kullanılmasını öneriyordu.
public interface MostlyOptionalCallback {
void onImportantAction();
default void onOptionalInformation() {
// Empty stub, this method is optional.
}
}
Engellemeyen bir işlev çağrısını modellerken android.os.OutcomeReceiver'ı kullanma
OutcomeReceiver<R,E>
, başarılı olduğunda R
, aksi takdirde E : Throwable
sonuç değerini raporlar. Bu, normal bir yöntem çağrısının yapabileceği şeylerle aynıdır. Sonuç döndüren veya istisna atan engelleyen bir yöntemi, engellemeyen bir ayarsız yönteme dönüştürürken geri çağırma türü olarak OutcomeReceiver
kullanın:
interface FooType {
// Before:
public FooResult requestFoo(FooRequest request);
// After:
public void requestFooAsync(FooRequest request, Executor executor,
OutcomeReceiver<FooResult, Throwable> callback);
}
Bu şekilde dönüştürülen eşzamansız yöntemler her zaman void
döndürür. requestFoo
'ün döndüreceği tüm sonuçlar, sağlanan executor
üzerinde çağrılarak requestFooAsync
'un callback
parametresinin OutcomeReceiver.onResult
değerine raporlanır.
requestFoo
tarafından oluşturulan istisnalar, OutcomeReceiver.onError
yöntemine aynı şekilde bildirilir.
Asynkron yöntem sonuçlarını bildirmek için OutcomeReceiver
kullanmak, androidx.core:core-ktx
'daki Continuation.asOutcomeReceiver
uzantısını kullanan asynkron yöntemler için Kotlin suspend fun
sarmalayıcısı da sağlar:
suspend fun FooType.requestFoo(request: FooRequest): FooResult =
suspendCancellableCoroutine { continuation ->
requestFooAsync(request, Runnable::run, continuation.asOutcomeReceiver())
}
Bu tür uzantılar, Kotlin istemcilerinin çağıran iş parçacığını engellemeden düz bir işlev çağrısının rahatlığıyla engellenmeyen ayarsız yöntemleri çağırmasına olanak tanır. Platform API'leri için bu 1'e 1 uzantılar, standart sürüm uyumluluk kontrolleri ve dikkat edilmesi gereken noktalarla birlikte Jetpack'teki androidx.core:core-ktx
yapının bir parçası olarak sunulabilir. Daha fazla bilgi, iptal ile ilgili dikkat edilmesi gereken noktalar ve örnekler için asOutcomeReceiver işlevinin belgelerine bakın.
Sonuç döndüren veya çalışması tamamlandığında istisna atan bir yöntemin semantikleriyle eşleşmeyen arayüz dışı yöntemler, geri çağırma türü olarak OutcomeReceiver
kullanmamalıdır. Bunun yerine, aşağıdaki bölümde listelenen diğer seçeneklerden birini kullanabilirsiniz.
Yeni tek soyut yöntem (SAM) türleri oluşturmak yerine işlevsel arayüzleri tercih edin.
API düzeyi 24'te, geri çağırma lambda'sı olarak kullanılmaya uygun Consumer<T>
gibi genel SAM arayüzleri sunan java.util.function.*
(referans dokümanları) türleri eklendi. Çoğu durumda, yeni SAM arayüzleri oluşturmak, Android API yüzey alanını gereksiz yere genişletirken tür güvenliği veya amaç iletişimi açısından çok az değer sağlar.
Yeni arayüzler oluşturmak yerine aşağıdaki genel arayüzleri kullanmayı düşünebilirsiniz:
Runnable
:() -> Unit
Supplier<R>
:() -> R
Consumer<T>
:(T) -> Unit
Function<T,R>
:(T) -> R
Predicate<T>
:(T) -> Boolean
- Referans dokümanlarda daha fazlası mevcuttur
SAM parametrelerinin yerleştirilmesi
Yöntem ek parametrelerle aşırı yüklenmiş olsa bile Kotlin'de idiomatik kullanımı etkinleştirmek için SAM parametreleri en sona yerleştirilmelidir.
public void schedule(Runnable runnable)
public void schedule(int delay, Runnable runnable)
Dokümanlar
Bunlar, API'lerin herkese açık dokümanlarıyla (Javadoc) ilgili kurallardır.
Tüm herkese açık API'ler belgelenmelidir
Herkese açık tüm API'lerde, geliştiricilerin API'yi nasıl kullanacağını açıklayan yeterli doküman bulunmalıdır. Geliştiricinin yöntemi otomatik tamamlama özelliğini kullanarak veya API referans dokümanlarına göz atarken bulduğunu ve bitişik API yüzeyinden (örneğin, aynı sınıf) minimum düzeyde bağlama sahip olduğunu varsayalım.
Yöntemler
Yöntem parametreleri ve döndürülen değerler, sırasıyla @param
ve @return
doküman ek açıklamaları kullanılarak belgelenmelidir. Javadoc gövdesini, "Bu yöntem..." ifadesinin öncesindeymiş gibi biçimlendirin.
Bir yöntemin parametre almadığı, özel dikkat gerektirmediği ve yöntem adının belirttiği işlevi döndürdüğü durumlarda @return
öğesini atlayabilir ve aşağıdakine benzer belgeler yazabilirsiniz:
/**
* Returns the priority of the thread.
*/
@IntRange(from = 1, to = 10)
public int getPriority() { ... }
Javadoc'da her zaman bağlantılar kullanın
Dokümanlar, ilgili sabitler, yöntemler ve diğer öğeler için diğer dokümanlara bağlantı vermelidir. Yalnızca düz metin kelimeleri değil, Javadoc etiketleri (ör. @see
ve {@link foo}
) kullanın.
Aşağıdaki kaynak örneği için:
public static final int FOO = 0;
public static final int BAR = 1;
Ham metin veya kod yazı tipi kullanmayın:
/**
* Sets value to one of FOO or <code>BAR</code>.
*
* @param value the value being set, one of FOO or BAR
*/
public void setValue(int value) { ... }
Bunun yerine bağlantıları kullanın:
/**
* Sets value to one of {@link #FOO} or {@link #BAR}.
*
* @param value the value being set
*/
public void setValue(@ValueType int value) { ... }
Bir parametrede @ValueType
gibi bir IntDef
ek açıklaması kullanıldığında, izin verilen türleri belirten dokümanların otomatik olarak oluşturulduğunu unutmayın. IntDef
hakkında daha fazla bilgi için notlar ile ilgili yönergeleri inceleyin.
Javadoc eklerken update-api veya docs target'i çalıştırma
Bu kural özellikle @link
veya @see
etiketleri eklerken önemlidir ve çıktının beklendiği gibi görünmesini sağlar. Javadoc'da ERROR çıkışı genellikle hatalı bağlantılardan kaynaklanır. Bu kontrolü update-api
veya docs
Oluştur hedefi gerçekleştirir. Ancak yalnızca Javadoc'ı değiştiriyorsanız ve update-api
hedefini çalıştırmanız gerekmiyorsa docs
hedefi daha hızlı olabilir.
Java değerlerini ayırt etmek için {@code foo} kullanın
true
, false
ve null
gibi Java değerlerini dokümanlar metninden ayırmak için {@code...}
ile kaydırın.
Kotlin kaynaklarında doküman yazarken, Markdown'da yaptığınız gibi kodu ters tırnak içine alabilirsiniz.
@param ve @return özetleri tek bir cümleden oluşmalıdır.
Parametre ve döndürülen değer özetleri küçük harfle başlamalı ve yalnızca tek bir cümle parçası içermelidir. Tek bir cümleden uzun ek bilgileriniz varsa bunları yöntem Javadoc gövdesine taşıyın:
/**
* @param e The element to be appended to the list. This must not be
* null. If the list contains no entries, this element will
* be added at the beginning.
* @return This method returns true on success.
*/
Şu şekilde değiştirilmelidir:
/**
* @param e element to be appended to this list, must be non-{@code null}
* @return {@code true} on success, {@code false} otherwise
*/
Dokümanlar ek açıklamaları için açıklamalar gerekli
@hide
ve @removed
ek açıklamalarının herkese açık API'den neden gizlendiğini belgeleyin.
@deprecated
ek açıklamasıyla işaretlenmiş API öğelerinin nasıl değiştirileceğine dair talimatlar ekleyin.
İstisnaları belgelemek için @throws kullanma
Bir yöntem, IOException
gibi kontrollü bir istisna atarsa istisnayı @throws
ile belgeleyin. Java istemcileri tarafından kullanılması amaçlanan Kotlin kaynaklı API'ler için işlevleri @Throws
ile ek açıklama ekleyin.
Bir yöntem, önlenebilir bir hatayı belirten işaretlenmemiş bir istisna (ör. IllegalArgumentException
veya IllegalStateException
) atarsa istisnanın neden atıldığını açıklayarak istisnayı belgeleyin. Atılan istisna, neden atıldığını da belirtmelidir.
Belirli işaretlenmemiş istisna durumları, gizli olarak kabul edilir ve belgelenmesi gerekmez. Örneğin, bir bağımsız değişkenin API sözleşmesini yöntem imzasına yerleştiren bir @IntDef
veya benzer bir ek açıklamayla eşleşmediği durumlarda NullPointerException
veya IllegalArgumentException
:
/**
* ...
* @throws IOException If it cannot find the schema for {@code toVersion}
* @throws IllegalStateException If the schema validation fails
*/
public SupportSQLiteDatabase runMigrationsAndValidate(String name, int version,
boolean validateDroppedTables, Migration... migrations) throws IOException {
// ...
if (!dbPath.exists()) {
throw new IllegalStateException("Cannot find the database file for " + name
+ ". Before calling runMigrations, you must first create the database "
+ "using createDatabase.");
}
// ...
Veya Kotlin'de:
/**
* ...
* @throws IOException If something goes wrong reading the file, such as a bad
* database header or missing permissions
*/
@Throws(IOException::class)
fun readVersion(databaseFile: File): Int {
// ...
val read = input.read(buffer)
if (read != 4) {
throw IOException("Bad database header, unable to read 4 bytes at " +
"offset 60")
}
}
// ...
Yöntem, istisna oluşturabilecek asenkron kod çağırıyorsa geliştiricinin bu istisnaları nasıl öğrendiğini ve bunlara nasıl yanıt verdiğini düşünün. Genellikle bu, istisnayı bir geri çağırma işlevine yönlendirmeyi ve bunları alan yöntemde oluşturulan istisnaları belgelemeyi içerir. Asynchronize istisnalar, ek açıklama eklenmiş yöntemden yeniden atılmadıkları sürece @throws
ile belgelenmemelidir.
Dokümanların ilk cümlesini noktayla bitirin
Doclava aracı, dokümanları basit bir şekilde ayrıştırır ve nokta (.) ile boşluk gördükten sonra özet dokümanını (sınıf dokümanlarının üst kısmındaki kısa açıklamada kullanılan ilk cümle) sonlandırır. Bu durum iki soruna yol açar:
- Bir kısa doküman noktayla bitmiyorsa ve bu üye, araç tarafından alınan devralınan dokümanlara sahipse özet, devralınan dokümanları da alır. Örneğin, özete eklenen boyut açıklamasını içeren
R.attr
dokümanlarındakiactionBarTabStyle
bölümüne bakın. - Doclava, özet dokümanlarını "ör." ile sonlandırdığı için ilk cümlede "ör." ifadesinden kaçının. Örneğin,
View.java
içindekiTEXT_ALIGNMENT_CENTER
'e bakın. Metalava, noktanın ardından satır sonuna kadar boşluk ekleyerek bu hatayı otomatik olarak düzeltir. Ancak bu hatayı baştan yapmamaya çalışın.
Dokümanları HTML olarak oluşturulacak şekilde biçimlendirme
Javadoc HTML olarak oluşturulur. Bu nedenle, bu dokümanları uygun şekilde biçimlendirin:
Satır sonlarında açık bir
<p>
etiketi kullanılmalıdır. Kapanış</p>
etiketi eklemeyin.Listeleri veya tabloları oluşturmak için ASCII kullanmayın.
Listeler sıralı ve sıralanmamış için sırasıyla
<ul>
veya<ol>
kullanmalıdır. Her öğe<li>
etiketiyle başlamalı ancak kapatıcı</li>
etiketine ihtiyaç duymaz. Son öğenin ardından kapatıcı</ul>
veya</ol>
etiketi gereklidir.Tablolarda satır için
<table>
, başlık için<tr>
ve hücre için<td>
kullanılmalıdır.<th>
Tüm tablo etiketleri için eşleşen kapatıcı etiketler gerekir. Desteği sonlandırılan etiketleri belirtmek için herhangi bir etiketteclass="deprecated"
kullanabilirsiniz.Satır içi kod yazı tipi oluşturmak için
{@code foo}
simgesini kullanın.Kod blokları oluşturmak için
<pre>
simgesini kullanın.<pre>
bloğundaki tüm metinler tarayıcı tarafından ayrıştırılır. Bu nedenle, parantezler<>
konusunda dikkatli olun. Bunları<
ve>
HTML varlıklarıyla kaçış karakteri olarak kullanabilirsiniz.Alternatif olarak, sorunlu bölümleri
{@code foo}
içine alırsanız kod snippet'inizde ham köşeli parantez<>
bırakabilirsiniz. Örneğin:<pre>{@code <manifest>}</pre>
API referans stil kılavuzuna uyun
Sınıf özetleri, yöntem açıklamaları, parametre açıklamaları ve diğer öğelerin stilinde tutarlılık sağlamak için Javadoc Aracı için Doküman Yorumları Yazma başlıklı makalede yer alan resmi Java dili yönergelerindeki önerileri uygulayın.
Android Framework'e özgü kurallar
Bu kurallar, Android çerçevesine yerleştirilmiş API'lere ve davranışlara (ör. Bundle
veya Parcelable
) özel API'ler, kalıplar ve veri yapılarıyla ilgilidir.
Amaç oluşturucular, create*Intent() kalıbını kullanmalıdır
Amaç için içerik üreticiler createFooIntent()
adlı yöntemleri kullanmalıdır.
Yeni genel amaçlı veri yapıları oluşturmak yerine Bundle'ı kullanın
İsteğe bağlı anahtar ile türetilmiş değer eşlemelerini temsil etmek için yeni genel amaçlı veri yapıları oluşturmaktan kaçının. Bunun yerine Bundle
kullanabilirsiniz.
Bu durum genellikle, platform dışı uygulamalar ve hizmetler arasında iletişim kanalı görevi gören platform API'leri yazılırken ortaya çıkar. Bu durumda platform, kanal üzerinden gönderilen verileri okumaz ve API sözleşmesi kısmen platformun dışında (ör. bir Jetpack kitaplığında) tanımlanabilir.
Platformun verileri okuduğu durumlarda Bundle
kullanmaktan kaçının ve güçlü şekilde yazılmış bir veri sınıfını tercih edin.
Paketlenebilir uygulamalarda herkese açık bir CREATOR alanı olmalıdır
Paketlenebilir enflasyon, ham oluşturucular aracılığıyla değil CREATOR
aracılığıyla gösterilir. Bir sınıf Parcelable
'ü uyguluyorsa CREATOR
alanı da herkese açık bir API olmalıdır ve Parcel
bağımsız değişkeni alan sınıf kurucusu özel olmalıdır.
Kullanıcı arayüzü dizeleri için CharSequence kullanın
Bir dize kullanıcı arayüzünde sunulduğunda Spannable
örneklerine izin vermek için CharSequence
kullanın.
Yalnızca bir anahtar veya kullanıcılar tarafından görülemeyen başka bir etiket ya da değerse String
kullanabilirsiniz.
Enums kullanmaktan kaçının
IntDef
, tüm platform API'lerinde enum yerine kullanılmalıdır ve paketlenmemiş kitaplık API'lerinde kesinlikle dikkate alınmalıdır. Yalnızca yeni değerlerin eklenmeyeceğinden emin olduğunuzda enum'leri kullanın.
IntDef
avantajları:
- Zaman içinde değer eklemeyi sağlar
- Platforma eklenen bir enum değeri nedeniyle artık kapsamlı olmayan Kotlin
when
ifadeleri çalışma zamanında başarısız olabilir.
- Platforma eklenen bir enum değeri nedeniyle artık kapsamlı olmayan Kotlin
- Çalışma zamanında sınıf veya nesne kullanılmaz, yalnızca ilkel veriler kullanılır
- R8 veya kod küçültme, paketlenmemiş kitaplık API'leri için bu maliyeti önleyebilir ancak bu optimizasyon, platform API sınıflarını etkileyemez.
Enum'ün avantajları
- Java ve Kotlin'in dil özelliği
- Kapsamlı switch,
when
ifadesi kullanımını etkinleştirir- Not: Değerler zaman içinde değişmemelidir. Önceki listeye bakın.
- Net kapsamlı ve bulunabilir adlandırma
- Derleme zamanında doğrulamayı etkinleştirir
- Örneğin, Kotlin'de değer döndüren bir
when
ifadesi
- Örneğin, Kotlin'de değer döndüren bir
- Arayüzleri uygulayabilen, statik yardımcılara sahip olabilen, üye veya uzantı yöntemlerini ve alanları gösterebilen çalışan bir sınıftır.
Android paketi katmanlama hiyerarşisini takip edin
android.*
paket hiyerarşisinde, alt düzey paketlerin üst düzey paketlere bağlı olamayacağı bir gizli sıralama vardır.
Google'dan, diğer şirketlerden ve ürünlerinden bahsetmeyin
Android platformu, tedarikçiden bağımsız olmayı amaçlayan açık kaynaklı bir projedir. API, genel olmalı ve gerekli izinlere sahip sistem entegratörleri veya uygulamalar tarafından eşit şekilde kullanılabilir olmalıdır.
Paketlenebilir uygulamalar nihai olmalıdır
Platform tarafından tanımlanan paketlenebilir sınıflar her zaman framework.jar
'ten yüklenir. Bu nedenle, bir uygulamanın Parcelable
uygulamasını geçersiz kılmaya çalışması geçersizdir.
Gönderen uygulama bir Parcelable
genişletirse alıcı uygulamada, paketi açmak için gönderenin özel uygulaması olmaz. Geriye dönük uyumlulukla ilgili not: Sınıfınız geçmişte nihai olmasa da herkese açık bir kurucusu yoksa sınıfınızı final
olarak işaretleyebilirsiniz.
Sistem işlemini çağıran yöntemler, RemoteException'ı RuntimeException olarak yeniden atmalıdır
RemoteException
genellikle dahili AIDL tarafından atılır ve sistem işleminin sona erdiğini veya uygulamanın çok fazla veri göndermeye çalıştığını gösterir. Her iki durumda da, uygulamaların güvenlik veya politika kararlarını sürdürmesini önlemek için herkese açık API, RuntimeException
olarak yeniden atılmalıdır.
Binder
çağrısının diğer tarafının sistem işlemi olduğunu biliyorsanız en iyi uygulama bu şablon koddur:
try {
...
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
API değişiklikleri için belirli istisnalar atma
Herkese açık API davranışları API düzeylerine göre değişebilir ve uygulama kilitlenmelerine neden olabilir (ör. yeni güvenlik politikalarını zorunlu kılmak için).
API'nin daha önce geçerli olan bir istek için atması gerektiğinde, genel bir istisna yerine yeni bir özel istisna atayın. Örneğin, SecurityException
yerine ExportedFlagRequired
(ve ExportedFlagRequired
, SecurityException
'u genişletebilir).
Bu, uygulama geliştiricilerin ve araçların API davranışı değişikliklerini tespit etmesine yardımcı olur.
Klonlama yerine kopyalama yapıcısını uygulayın
Object
sınıfı tarafından sağlanan API sözleşmelerinin olmaması ve clone()
kullanan sınıfların genişletilmesiyle ilgili zorluklar nedeniyle Java clone()
yönteminin kullanılması önerilmez. Bunun yerine, aynı türde bir nesne alan bir kopyalama oluşturucu kullanın.
/**
* Constructs a shallow copy of {@code other}.
*/
public Foo(Foo other)
Oluşturma için Builder kullanan sınıflar, kopyada değişiklik yapılmasına izin vermek üzere bir Builder kopya kurucu eklemeyi düşünmelidir.
public class Foo {
public static final class Builder {
/**
* Constructs a Foo builder using data from {@code other}.
*/
public Builder(Foo other)
FileDescriptor yerine ParcelFileDescriptor kullanma
java.io.FileDescriptor
nesnesinin sahiplik tanımı zayıftır. Bu durum, kapatıldıktan sonra kullanımla ilgili anlaşılması zor hatalara neden olabilir. Bunun yerine API'ler ParcelFileDescriptor
örnekleri döndürmeli veya kabul etmelidir. Eski kod, gerekirse dup() veya getFileDescriptor() kullanılarak PFD ile FD arasında dönüştürme yapabilir.
Tuhaf boyutlu sayısal değerler kullanmaktan kaçının
short
veya byte
değerlerini doğrudan kullanmaktan kaçının. Bu değerler genellikle API'yi gelecekte nasıl geliştirebileceğinizi sınırlandırır.
BitSet kullanmaktan kaçının
java.util.BitSet
, uygulama için mükemmel olsa da herkese açık API için uygun değildir. Değiştirilebilir, yüksek sıklıkta yöntem çağrıları için ayırma gerektirir ve her bitin temsil ettiği şey için anlamsal anlam sağlamaz.
Yüksek performanslı senaryolar için @IntDef
ile birlikte bir int
veya long
kullanın. Düşük performanslı senaryolar için Set<EnumType>
kullanabilirsiniz. Ham ikili veriler için byte[]
kullanın.
android.net.Uri'yi tercih edin
android.net.Uri
, Android API'lerindeki URI'ler için tercih edilen kapsülleme yöntemidir.
URI'leri ayrıştırırken aşırı katı olduğu için java.net.URI
'ten kaçının ve eşitlik tanımı ciddi şekilde bozuk olduğu için java.net.URL
'ü asla kullanmayın.
@IntDef, @LongDef veya @StringDef olarak işaretlenmiş ek açıklamaları gizleme
@IntDef
, @LongDef
veya @StringDef
olarak işaretlenmiş ek açıklamalar, bir API'ye iletilebilecek geçerli sabitler kümesini belirtir. Ancak bunlar API olarak dışa aktarıldığında derleyici sabitleri satır içi hale getirir ve ek açıklamanın API taslağında (platform için) veya JAR'ında (kitaplıklar için) yalnızca (artık işe yaramayan) değerler kalır.
Bu nedenle, bu ek açıklamaların kullanımları platformda @hide
docs ek açıklaması veya kitaplıklarda @RestrictTo.Scope.LIBRARY)
kod ek açıklaması ile işaretlenmelidir. API taslakları veya JAR'larda görünmelerini önlemek için her iki durumda da @Retention(RetentionPolicy.SOURCE)
ile işaretlenmelidir.
@RestrictTo(RestrictTo.Scope.LIBRARY)
@Retention(RetentionPolicy.SOURCE)
@IntDef({
STREAM_TYPE_FULL_IMAGE_DATA,
STREAM_TYPE_EXIF_DATA_ONLY,
})
public @interface ExifStreamType {}
Platform SDK'sı ve kitaplık AAR'ları oluşturulurken bir araç ek açıklamaları ayıklayıp derlenmiş kaynaklardan ayrı olarak paketler. Android Studio bu paketlenmiş biçimi okur ve tür tanımlarını uygular.
Yeni ayar sağlayıcı anahtarları eklemeyin
Settings.Global
, Settings.System
veya Settings.Secure
kaynaklı yeni anahtarları göstermeyin.
Bunun yerine, ilgili sınıfa (genellikle bir "yönetici" sınıfı) uygun bir alıcı ve ayarlayıcı Java API'si ekleyin. Gerektiği gibi istemcileri değişikliklerden haberdar etmek için bir dinleyici mekanizması veya yayın ekleyin.
SettingsProvider
ayarlarının, alıcı/ayarlayıcılara kıyasla birtakım sorunları vardır:
- Tip güvenliği yoktur.
- Varsayılan değer sağlamanın birleşik bir yolu yoktur.
- İzinleri özelleştirmenin uygun bir yolu yok.
- Örneğin, ayarınızı özel izinle korumak mümkün değildir.
- Özel mantığı doğru şekilde eklemenin uygun bir yolu yok.
- Örneğin, A ayarının değerini B ayarının değerine göre değiştirmek mümkün değildir.
Örnek:
Settings.Secure.LOCATION_MODE
uzun zamandır var ancak konum ekibi, LocationManager.isLocationEnabled()
doğru Java API'si ve MODE_CHANGED_ACTION
yayını için desteğini sonlandırdı. Bu sayede ekibe çok daha fazla esneklik sağlandı ve API'lerin semantikleri artık çok daha net.
Activity ve AsyncTask'ı genişletmeyin
AsyncTask
, uygulama ayrıntısıdır. Bunun yerine bir dinleyici veya androidx'de ListenableFuture
API'si gösterin.
Activity
alt sınıfları oluşturulamaz. Özelliğinizin etkinliğini uzatmak, kullanıcıların aynı işlemi yapmasını gerektiren diğer özelliklerle uyumsuz hale getirir. Bunun yerine, LifecycleObserver gibi araçları kullanarak kompozisyona güvenin.
Bağlamı'n getUser() işlevini kullanın
Context
'ye bağlı sınıflar (ör. Context.getSystemService()
'ten döndürülen her şey), belirli kullanıcıları hedefleyen üyeleri göstermek yerine Context
'ye bağlı kullanıcıyı kullanmalıdır.
class FooManager {
Context mContext;
void fooBar() {
mIFooBar.fooBarForUser(mContext.getUser());
}
}
class FooManager {
Context mContext;
Foobar getFoobar() {
// Bad: doesn't appy mContext.getUserId().
mIFooBar.fooBarForUser(Process.myUserHandle());
}
Foobar getFoobar() {
// Also bad: doesn't appy mContext.getUserId().
mIFooBar.fooBar();
}
Foobar getFoobarForUser(UserHandle user) {
mIFooBar.fooBarForUser(user);
}
}
İstisna: Bir yöntem, tek bir kullanıcıyı temsil etmeyen değerler (ör. UserHandle.ALL
) kabul ediyorsa kullanıcı bağımsız değişkeni kabul edebilir.
Basit int yerine UserHandle kullanın
Tür güvenliği sağlamak ve kullanıcı kimliklerini uid ile karıştırmamak için UserHandle
tercih edilir.
Foobar getFoobarForUser(UserHandle user);
Foobar getFoobarForUser(int userId);
Kaçınılmaz olduğu durumlarda, kullanıcı kimliğini temsil eden bir int
, @UserIdInt
ile ek açıklamaya tabi tutulmalıdır.
Foobar getFoobarForUser(@UserIdInt int user);
Niyetleri yayınlamak için dinleyicileri veya geri çağırma işlevlerini tercih etme
Yayın intent'leri çok güçlüdür ancak sistem sağlığını olumsuz yönde etkileyebilecek yeni davranışlara yol açmıştır. Bu nedenle, yeni yayın intent'leri dikkatli bir şekilde eklenmelidir.
Yeni yayın niyetinin kullanıma sunulmasını önermememize neden olan bazı endişeleri aşağıda bulabilirsiniz:
FLAG_RECEIVER_REGISTERED_ONLY
işareti olmadan yayın gönderirken, çalışmayan uygulamaları zorla başlatır. Bu bazen istenen bir sonuç olsa da düzinelerce uygulamanın kaldırılmasına neden olarak sistem sağlığını olumsuz yönde etkileyebilir. Çeşitli ön koşullar karşılandığında daha iyi koordinasyon sağlamak içinJobScheduler
gibi alternatif stratejiler kullanmanızı öneririz.Yayın gönderirken uygulamalara sunulan içeriği filtreleme veya ayarlama olanağı çok azdır. Bu durum, gelecekteki gizlilikle ilgili endişelere yanıt vermeyi veya alıcı uygulamanın hedef SDK'sına göre davranış değişiklikleri uygulamayı zorlaştırır ya da imkansız hale getirir.
Yayın sıraları ortak bir kaynak olduğundan aşırı yüklenebilir ve etkinliğinizin zamanında yayınlanmasına neden olmayabilir. Kullanımda olan ve uçtan uca gecikmesi 10 dakika veya daha uzun olan birkaç yayın kuyruğu gözlemledik.
Bu nedenlerden dolayı, yeni özelliklerde yayın intent'leri yerine dinleyiciler veya geri çağırma işlevleri ya da JobScheduler
gibi diğer olanakları kullanmayı düşünmenizi öneririz.
Yayın intent'lerinin ideal tasarım olmaya devam ettiği durumlarda dikkate alınması gereken bazı en iyi uygulamalar şunlardır:
- Mümkünse yayınınızı, zaten çalışan uygulamalarla sınırlamak için
Intent.FLAG_RECEIVER_REGISTERED_ONLY
simgesini kullanın. Örneğin,ACTION_SCREEN_ON
uygulamaları uyandırmamak için bu tasarımı kullanır. - Mümkünse yayını ilgilendiğiniz belirli bir uygulamada hedeflemek için
Intent.setPackage()
veyaIntent.setComponent()
öğesini kullanın. Örneğin,ACTION_MEDIA_BUTTON
oynatma kontrollerini yöneten mevcut uygulamaya odaklanmak için bu tasarımı kullanır. - Mümkünse kötü amaçlı uygulamaların OS kimliğine bürünmesini önlemek için yayınınızı
<protected-broadcast>
olarak tanımlayın.
Sisteme bağlı geliştirici hizmetlerinde intent'ler
Geliştirici tarafından genişletilmesi ve sisteme bağlı olması amaçlanan hizmetler (ör. NotificationListenerService
gibi soyut hizmetler) sistemden gelen bir Intent
işlemine yanıt verebilir. Bu tür hizmetler aşağıdaki ölçütleri karşılamalıdır:
- Sınıfta, hizmetin tam nitelikli sınıf adını içeren bir
SERVICE_INTERFACE
dize sabit değeri tanımlayın. Bu sabit,@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
ile ek açıklamaya tabi tutulmalıdır. - Sınıfta, geliştiricilerin platformdan Intent almak için
AndroidManifest.xml
'larına<intent-filter>
eklemesi gerektiğini belirtin. - Kötü amaçlı uygulamaların geliştirici hizmetlerine
Intent
göndermesini önlemek için sistem düzeyinde bir izin eklemenizi önemle tavsiye ederiz.
Kotlin-Java birlikte çalışabilirliği
Yönergelerin tam listesi için resmi Android Kotlin-Java birlikte çalışabilirlik kılavuzuna bakın. Keşfedilebilirliği artırmak için belirli kurallar bu kılavuza kopyalanmıştır.
API görünürlüğü
suspend fun
gibi bazı Kotlin API'leri Java geliştiricileri tarafından kullanılmak üzere tasarlanmamıştır. Ancak @JvmSynthetic
kullanarak dile özgü görünürlüğü kontrol etmeye çalışmayın. Bu, API'nin hata ayıklayıcılarda nasıl sunulduğuyla ilgili yan etkilere neden olarak hata ayıklama işlemini daha da zorlaştırır.
Ayrıntılı bilgi için Kotlin-Java birlikte çalışabilirlik kılavuzuna veya Asenkron kılavuzuna bakın.
Tamamlayıcı nesneler
Kotlin, statik üyeleri göstermek için companion object
kullanır. Bazı durumlarda bu sınıflar, Java'dan içeren sınıf yerine Companion
adlı bir iç sınıfta gösterilir. Companion
sınıfları, API metin dosyalarında boş sınıflar olarak görünebilir. Bu durum, amaçlandığı gibi çalışır.
Java ile uyumluluğu en üst düzeye çıkarmak için tamamlayıcı nesnelerin sabit olmayan alanlarını @JvmField
ile, herkese açık işlevlerini ise @JvmStatic
ile ekleyerek doğrudan kapsayıcı sınıfta gösterin.
companion object {
@JvmField val BIG_INTEGER_ONE = BigInteger.ONE
@JvmStatic fun fromPointF(pointf: PointF) {
/* ... */
}
}
Android platform API'lerinin evrimi
Bu bölümde, mevcut Android API'lerinde ne tür değişiklikler yapabileceğiniz ve mevcut uygulamalarla ve kod tabanlarıyla uyumluluğu en üst düzeye çıkarmak için bu değişiklikleri nasıl uygulamanız gerektiğiyle ilgili politikalar açıklanmaktadır.
İkili kodda bozulmalara neden olan değişiklikler
Tamamlanmış herkese açık API yüzeylerinde ikili kodda bozulmalara neden olan değişikliklerden kaçının. Bu tür değişiklikler genellikle make update-api
çalıştırıldığında hatalara neden olur ancak Metalava'nın API kontrolünün yakalayamadığı uç durumlar olabilir. Şüphe duyduğunuzda, Java'da hangi API değişikliklerinin uyumlu olduğuna dair ayrıntılı bir açıklama için Eclipse Foundation'ın Java tabanlı API'lerin evrimi kılavuzuna bakın. Gizli (ör. sistem) API'lerde ikili kodda bozulmalara neden olan değişiklikler, desteği sonlandırma/değiştirme döngüsünü izlemelidir.
Kaynakta bozulmalara neden olan değişiklikler
İkili kodda değişiklik yapmasa bile kaynakta değişiklik yapmanızı önermeyiz. İkili uyumlu ancak kaynakta değişiklik gerektiren bir değişikliğe örnek olarak, mevcut bir sınıfa genel bir sınıf eklemek verilebilir. Bu değişiklik ikili uyumludur ancak devralma veya belirsiz referanslar nedeniyle derleme hataları oluşturabilir.
Kaynakta kesinti yaratan değişiklikler make update-api
çalıştırıldığında hata oluşturmaz. Bu nedenle, mevcut API imzalarındaki değişikliklerin etkisini anlamak için dikkatli olmanız gerekir.
Bazı durumlarda, geliştirici deneyimini veya kod doğruluğunu iyileştirmek için kaynakta değişiklik yapılması gerekir. Örneğin, Java kaynaklarına boşluk kabul edilebilirlik ek açıklamaları eklemek Kotlin koduyla birlikte çalışabilirliği iyileştirir ve hata olasılığını azaltır ancak genellikle kaynak kodunda değişiklik (bazen önemli değişiklikler) yapılmasını gerektirir.
Gizli API'lerde yapılan değişiklikler
@TestApi
ile ek açıklama eklenmiş API'leri dilediğiniz zaman değiştirebilirsiniz.
@SystemApi
ile ek açıklama eklenmiş API'leri üç yıl boyunca saklamanız gerekir. Bir sistem API'sini aşağıdaki programa göre kaldırmanız veya yeniden düzenlemeniz gerekir:
- API y - Eklendi
- API y+1 - Kullanımdan Kaldırma
- Kodu
@Deprecated
ile işaretleyin. - Değiştirme ekleyin ve
@deprecated
doküman ek açıklamasını kullanarak desteği sonlandırılan kodun Javadoc'ındaki değişime bağlantı verin. - Geliştirme döngüsü sırasında, API'nin desteğinin sonlandırıldığını kurum içi kullanıcılara bildiren hatalar gönderin. Bu, değişim API'lerinin yeterli olduğunu doğrulamaya yardımcı olur.
- Kodu
- API y+2 - Yumuşak kaldırma
- Kodu
@removed
ile işaretleyin. - İsteğe bağlı olarak, sürüm için geçerli SDK düzeyini hedefleyen uygulamalarda throw veya no-op.
- Kodu
- API y+3 - Kalıcı kaldırma
- Kodu kaynak ağacından tamamen kaldırın.
Desteği sonlandırma
Desteği sonlandırmayı bir API değişikliği olarak değerlendiririz ve bu, büyük bir sürümde (ör. mektup) gerçekleşebilir. API'lerin desteğini sonlandırırken @Deprecated
kaynak notunu ve @deprecated
<summary>
doküman notunu birlikte kullanın. Özetiniz bir taşıma stratejisi içermelidir. Bu strateji, alternatif bir API'ye bağlantı verebilir veya API'yi neden kullanmamanız gerektiğini açıklayabilir:
/**
* Simple version of ...
*
* @deprecated Use the {@link androidx.fragment.app.DialogFragment}
* class with {@link androidx.fragment.app.FragmentManager}
* instead.
*/
@Deprecated
public final void showDialog(int id)
Ayrıca, android.R
sınıfında sunulan özellikler ve stillenebilir özellikler de dahil olmak üzere XML'de tanımlanan ve Java'da sunulan API'lerin desteğini de bir özetle birlikte sonlandırmanız gerekir:
<!-- Attribute whether the accessibility service ...
{@deprecated Not used by the framework}
-->
<attr name="canRequestEnhancedWebAccessibility" format="boolean" />
API'lerin desteği ne zaman sonlandırılır?
Destek sonlandırma bildirimleri, bir API'nin yeni kodda kullanılmasını engellemek için en yararlı yöntemdir.
Ayrıca, API'lerin @removed
olarak işaretlenmesini de zorunlu tutuyoruz. Ancak bu, geliştiricilerin halihazırda kullandıkları API'den geçiş yapmaları için güçlü bir motivasyon sağlamıyor.@deprecated
Bir API'nin desteğini sonlandırmadan önce geliştiriciler üzerindeki etkisini göz önünde bulundurun. Bir API'nin desteğinin sonlandırılmasının etkileri şunlardır:
javac
, derleme sırasında uyarı verir.- Desteği sonlandırılan API'ler için verilen uyarılar genel olarak engellenemez veya temel değer olarak ayarlanamaz. Bu nedenle,
-Werror
kullanan geliştiricilerin derleme SDK sürümlerini güncelleyebilmeleri için desteği sonlandırılan API'nin her kullanımını tek tek düzeltmesi veya engellemesi gerekir. - Desteği sonlandırılan sınıfların içe aktarılmasıyla ilgili desteği sonlandırıldı uyarıları devre dışı bırakılamaz. Sonuç olarak, geliştiricilerin derleme SDK sürümlerini güncelleyebilmeleri için desteği sonlandırılan bir sınıfın her kullanımında tam nitelikli sınıf adını satır içi olarak eklemeleri gerekir.
- Desteği sonlandırılan API'ler için verilen uyarılar genel olarak engellenemez veya temel değer olarak ayarlanamaz. Bu nedenle,
d.android.com
ile ilgili belgelerde desteğin sonlandırıldığına dair bir bildirim gösteriliyor.- Android Studio gibi IDE'ler, API kullanım sitesinde bir uyarı gösterir.
- IDE'ler, API'yi otomatik tamamlama özelliğinde alt sıralara yerleştirebilir veya gizleyebilir.
Sonuç olarak, bir API'nin desteğinin sonlandırılması, kod sağlığıyla en çok ilgilenen geliştiricilerin (-Werror
kullananlar) yeni SDK'ları kullanmaya başlamasını engelleyebilir.
Mevcut kodlarındaki uyarılarla ilgilenmeyen geliştiriciler, desteği sonlandırılan özellikleri tamamen göz ardı edebilir.
Çok sayıda desteği sonlandırılan API sunan bir SDK, bu iki durumu da daha da kötüleştirir.
Bu nedenle, API'lerin desteğinin sonlandırılmasını yalnızca aşağıdaki durumlarda öneririz:
- API'yi gelecekteki bir sürümde
@remove
yapmayı planlıyoruz. - API kullanımı, uyumluluğu bozmadan düzeltemediğimiz yanlış veya tanımlanmamış davranışlara yol açıyor.
Bir API'nin desteğini sonlandırıp yeni bir API ile değiştirdiğinizde, hem eski hem de yeni cihazları desteklemeyi kolaylaştırmak için androidx.core
gibi bir Jetpack kitaplığına ilgili uyumluluk API'sini eklemenizi önemle tavsiye ederiz.
Mevcut ve gelecekteki sürümlerde amaçlandığı gibi çalışan API'lerin desteğini sonlandırmanızı önermeyiz:
/**
* ...
* @deprecated Use {@link #doThing(int, Bundle)} instead.
*/
@Deprecated
public void doThing(int action) {
...
}
public void doThing(int action, @Nullable Bundle extras) {
...
}
API'lerin artık belgelenmiş davranışlarını sürdüremediği durumlarda desteğin sonlandırılması uygundur:
/**
* ...
* @deprecated No longer displayed in the status bar as of API 21.
*/
@Deprecated
public RemoteViews tickerView;
Desteği sonlandırılan API'lerde yapılan değişiklikler
Kullanımdan kaldırılmış API'lerin davranışını korumanız gerekir. Bu, test uygulamalarının aynı kalması ve API'nin desteğini sonlandırdıktan sonra testlerin geçmeye devam etmesi gerektiği anlamına gelir. API'de test yoksa test eklemeniz gerekir.
Desteği sonlandırılan API yüzeylerini gelecekteki sürümlerde genişletmeyin. Desteği sonlandırılmış mevcut bir API'ye lint doğruluğu ek açıklamaları (ör. @Nullable
) ekleyebilirsiniz ancak yeni API'ler eklememeniz gerekir.
Desteği sonlandırılan olarak yeni API'ler eklemeyin. Bir ön sürüm döngüsü içinde eklenen ve daha sonra desteği sonlandırılan API'ler varsa (bu nedenle, herkese açık API yüzeyine başlangıçta desteği sonlandırılmış olarak girerler) API'yi tamamlamadan önce bunları kaldırmanız gerekir.
Kısmi olarak kaldırma
Yumuşak kaldırma, kaynağı bozan bir değişikliktir ve API Konseyi açıkça onaylamadığı sürece herkese açık API'lerde bu değişikliği yapmamalısınız.
Sistem API'leri için, yumuşak kaldırmadan önce API'yi önemli bir sürümün süresi boyunca kullanımdan kaldırmanız gerekir. API'lere yönelik tüm doküman referanslarını kaldırın ve API'leri yumuşak kaldırırken @removed <summary>
doküman ek açıklamalarını kullanın. Özetiniz, kaldırma nedenini içermelidir ve Destek sonu bölümünde açıklandığı gibi bir taşıma stratejisi içerebilir.
Yumuşak kaldırılan API'lerin davranışı olduğu gibi korunabilir ancak daha da önemlisi, mevcut istemcilerin API'yi çağırırken kilitlenmemesi için korunmalıdır. Bazı durumlarda bu, davranışı korumak anlamına gelebilir.
Test kapsamı korunmalıdır ancak davranış değişikliklerine uyum sağlamak için testlerin içeriğinin değiştirilmesi gerekebilir. Testler, mevcut arayanların çalışma zamanında kilitlenmemesini doğrulamaya devam etmelidir. Yumuşak kaldırılan API'lerin davranışını olduğu gibi koruyabilirsiniz ancak daha da önemlisi, mevcut arayanların API'yi çağırırken kilitlenmemesi için davranışı korumanız gerekir. Bazı durumlarda bu, davranışı korumak anlamına gelebilir.
Test kapsamını korumanız gerekir ancak testlerin içeriğinin, davranış değişikliklerine uyum sağlamak için değiştirilmesi gerekebilir. Testler, mevcut arayanların çalışma zamanında kilitlenmemesini doğrulamaya devam etmelidir.
Teknik düzeyde, API'yi @remove
Javadoc ek açıklamasını kullanarak SDK stub JAR'ından ve derleme zamanı sınıf yolu klasöründen kaldırırız ancak API, @hide
API'lerine benzer şekilde çalışma zamanı sınıf yolu klasöründe kalır:
/**
* Ringer volume. This is ...
*
* @removed Not functional since API 2.
*/
public static final String VOLUME_RING = ...
Uygulama geliştirici açısından, API artık otomatik tamamlamada görünmez ve compileSdk
, API'nin kaldırıldığı SDK'ya eşit veya SDK'dan sonraki bir sürüm olduğunda API'ye referans veren kaynak kod derlenmez. Ancak kaynak kod, önceki SDK'lara göre başarılı bir şekilde derlenmeye devam eder ve API'ye referans veren ikili dosyalar çalışmaya devam eder.
Belirli API kategorileri yumuşak olarak kaldırılmamalıdır. Belirli API kategorilerini yumuşak kaldırmamalısınız.
Soyut yöntemler
Geliştiricilerin genişletebileceği sınıflardaki soyut yöntemleri yumuşak olarak kaldırmamanız gerekir. Bu, geliştiricilerin sınıfı tüm SDK düzeylerinde başarılı bir şekilde genişletmesini imkansız hale getirir.
Geliştiricilerin bir sınıfı hiç ve gelecekte genişletemeyeceği nadir durumlarda bile soyut yöntemleri yumuşak olarak kaldırabilirsiniz.
Kalıcı kaldırma
Kaldırma işlemi, ikili kodda değişiklik yapan bir işlemdir ve hiçbir zaman herkese açık API'lerde yapılmamalıdır.
Önerilmeyen ek açıklama
@Discouraged
ek açıklamasını, çoğu durumda (>%95) bir API'yi önermediğimizi belirtmek için kullanırız. Kullanılması önerilmez API'ler, kullanımdan kaldırılmasını engelleyen dar bir kritik kullanım alanı olması açısından kullanımdan kaldırılmış API'lerden farklıdır. Bir API'yi "kullanılması önerilmez" olarak işaretlediğinizde açıklama ve alternatif bir çözüm sağlamanız gerekir:
@Discouraged(message = "Use of this function is discouraged because resource
reflection makes it harder to perform build
optimizations and compile-time verification of code. It
is much more efficient to retrieve resources by
identifier (such as `R.foo.bar`) than by name (such as
`getIdentifier()`)")
public int getIdentifier(String name, String defType, String defPackage) {
return mResourcesImpl.getIdentifier(name, defType, defPackage);
}
Önerilmeyen yeni API'ler eklememeniz gerekir.
Mevcut API'lerin davranışında yapılan değişiklikler
Bazı durumlarda, mevcut bir API'nin uygulama davranışını değiştirmek isteyebilirsiniz. Örneğin, Android 7.0'da geliştiricilerin Binder
üzerinden gönderilemeyecek kadar büyük etkinlikler yayınlamaya çalıştıklarında bunu net bir şekilde bildirmek için DropBoxManager
'ü iyileştirdik.
Ancak mevcut uygulamalarda sorun yaşanmaması için eski uygulamalarda güvenli davranışın korunmasını önemle tavsiye ederiz. Bu davranış değişikliklerini geçmişte uygulamanın ApplicationInfo.targetSdkVersion
'sine göre kontrol ediyorduk ancak kısa süre önce Uygulama Uyumluluğu Çerçevesi'nin kullanılmasını zorunlu kılacak şekilde geçiş yaptık. Bu yeni çerçeveyi kullanarak davranış değişikliğinin nasıl uygulanacağına dair bir örnek aşağıda verilmiştir:
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
public class MyClass {
@ChangeId
// This means the change will be enabled for target SDK R and higher.
@EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.R)
// Use a bug number as the value, provide extra detail in the bug.
// FOO_NOW_DOES_X will be the change name, and 123456789 the change ID.
static final long FOO_NOW_DOES_X = 123456789L;
public void doFoo() {
if (CompatChanges.isChangeEnabled(FOO_NOW_DOES_X)) {
// do the new thing
} else {
// do the old thing
}
}
}
Bu Uygulama Uyumluluk Çerçevesi tasarımını kullanmak, geliştiricilerin uygulamalarında hata ayıklama kapsamında, düzinelerce davranış değişikliğine aynı anda uyum sağlamaları yerine, önizleme ve beta sürümleri sırasında belirli davranış değişikliklerini geçici olarak devre dışı bırakmalarına olanak tanır.
İleriye dönük uyumluluk
İleriye dönük uyumluluk, bir sistemin kendi sonraki bir sürümü için tasarlanmış girişleri kabul etmesine olanak tanıyan bir tasarım özelliğidir. API tasarımında, geliştiricilerin kodu bir kez yazıp bir kez test etmesini ve her yerde sorunsuz şekilde çalıştırmasını beklediği için hem ilk tasarıma hem de gelecekteki değişikliklere özellikle dikkat etmeniz gerekir.
Android'de en yaygın ileriye dönük uyumluluk sorunlarına aşağıdakiler neden olur:
- Daha önce tamamlanmış olduğu varsayılan bir kümeye (ör.
@IntDef
veyaenum
) yeni sabitler ekleme (örneğin,switch
'de istisna atan birdefault
varsa). - Doğrudan API yüzeyinde yakalanmayan bir özellik için destek ekleme (örneğin, daha önce yalnızca
<color>
kaynaklarının desteklendiği XML'deColorStateList
türü kaynaklar atama desteği). - Çalışma zamanındaki kontrollerle ilgili kısıtlamaları gevşetme (ör. eski sürümlerde bulunan bir
requireNotNull()
kontrolünü kaldırma).
Tüm bu durumlarda geliştiriciler, bir sorun olduğunu yalnızca çalıştırma sırasında anlar. Daha da kötüsü, bu durumu sahadaki eski cihazlardan gelen kilitlenme raporları sonucunda öğrenebilirler.
Ayrıca, bu örneklerin tümü teknik olarak geçerli API değişiklikleridir. Bu sorunlar ikili veya kaynak uyumluluğunu bozmaz ve API lint bu sorunların hiçbirini yakalamaz.
Bu nedenle, API tasarımcıları mevcut sınıfları değiştirirken dikkatli olmalıdır. "Bu değişiklik, yalnızca platformun en son sürümüne göre yazılan ve test edilen kodun daha eski sürümlerde başarısız olmasına neden olacak mı?" sorusunu sorun.
XML şemaları
Bir XML şeması, bileşenler arasında kararlı bir arayüz görevi görüyorsa bu şema açıkça belirtilmeli ve diğer Android API'lerine benzer şekilde geriye dönük uyumlu bir şekilde gelişmelidir. Örneğin, XML öğelerinin ve özelliklerinin yapısı, diğer Android API yüzeylerinde yöntemlerin ve değişkenlerin korunmasına benzer şekilde korunmalıdır.
XML desteğinin sonlandırılması
Bir XML öğesinin veya özelliğinin desteğini sonlandırmak istiyorsanız xs:annotation
işaretçisini ekleyebilirsiniz ancak tipik @SystemApi
evrim yaşam döngüsünü izleyerek mevcut XML dosyalarını desteklemeye devam etmeniz gerekir.
<xs:element name="foo">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string">
<xs:annotation name="Deprecated"/>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
Öğe türleri korunmalıdır
Şemalar, complexType
öğesinin alt öğeleri olarak sequence
öğesini, choice
öğesini ve all
öğelerini destekler. Ancak bu alt öğeler, alt öğelerinin sayısı ve sırası açısından farklılık gösterir. Bu nedenle, mevcut bir türün değiştirilmesi uyumlu olmayan bir değişiklik olur.
Mevcut bir türü değiştirmek istiyorsanız en iyi uygulama, eski türün desteğini sonlandırmak ve yerine yeni bir tür tanıtmaktır.
<!-- Original "sequence" value -->
<xs:element name="foo">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string">
<xs:annotation name="Deprecated"/>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- New "choice" value -->
<xs:element name="fooChoice">
<xs:complexType>
<xs:choice>
<xs:element name="name" type="xs:string"/>
</xs:choice>
</xs:complexType>
</xs:element>
Ana hatlara özgü kalıplar
Mainline, sistem görüntüsünün tamamını güncellemek yerine Android OS'in alt sistemlerini ("ana hat modülleri") tek tek güncellemeye olanak tanıyan bir projedir.
Ana hat modüllerinin ana platformdan "paketinin kaldırılması" gerekir. Yani her modül ile dünyanın geri kalanı arasındaki tüm etkileşimlerin resmi (herkese açık veya sistem) API'ler kullanılarak yapılması gerekir.
Ana hat modüllerinin uyması gereken belirli tasarım kalıpları vardır. Bu bölümde bunlar açıklanmaktadır.
<Module>FrameworkInitializer kalıbı
Bir ana hat modülünün @SystemService
sınıflarını (örneğin, JobScheduler
) göstermesi gerekiyorsa aşağıdaki kalıbı kullanın:
Modülünüzde bir
<YourModule>FrameworkInitializer
sınıfı gösterin. Bu sınıfın$BOOTCLASSPATH
içinde olması gerekir. Örnek: StatsFrameworkInitializer@SystemApi(client = MODULE_LIBRARIES)
ile işaretleyin.public static void registerServiceWrappers()
yöntemi ekleyin.Bir
Context
referansı gerektiren bir hizmet yöneticisi sınıfını kaydetmek içinSystemServiceRegistry.registerContextAwareService()
kullanın.Context
referansı gerektirmeyen bir hizmet yöneticisi sınıfını kaydetmek içinSystemServiceRegistry.registerStaticService()
kullanın.SystemServiceRegistry
sınıfının statik başlatıcısındanregisterServiceWrappers()
yöntemini çağırın.
<Module>ServiceManager kalıbı
Normalde, sistem hizmeti bağlayıcı nesnelerini kaydetmek veya bunlara referans almak için ServiceManager
kullanılır ancak ana hat modülleri gizli olduğu için bunu kullanamaz. Ana hat modüllerinin statik platform veya diğer modüller tarafından sunulan sistem hizmeti bağlayıcı nesnelerini kaydettirmesi veya referans vermesi gerekmediğinden bu sınıf gizlidir.
Ana hat modülleri, modül içinde uygulanan bağlayıcı hizmetlerine kaydolabilmek ve bu hizmetlere referans alabilmek için aşağıdaki kalıbı kullanabilir.
TelephonyServiceManager'ın tasarımını temel alan bir
<YourModule>ServiceManager
sınıfı oluşturunSınıfı
@SystemApi
olarak gösterin. Yalnızca$BOOTCLASSPATH
sınıflarından veya sistem sunucusu sınıflarından erişmeniz gerekiyorsa@SystemApi(client = MODULE_LIBRARIES)
kullanabilirsiniz. Aksi takdirde@SystemApi(client = PRIVILEGED_APPS)
işe yarar.Bu sınıf aşağıdakilerden oluşur:
- Gizli bir kurucu olduğundan yalnızca statik platform kodu bu sınıfı örnekleyebilir.
- Belirli bir ad için
ServiceRegisterer
örneği döndüren herkese açık alıcı yöntemleri. Bir bağlayıcı nesnenize sahipseniz bir alıcı yöntemine ihtiyacınız vardır. İki tane varsa iki tane alıcıya ihtiyacınız vardır. ActivityThread.initializeMainlineModules()
içinde bu sınıfı örneklendirin ve modülünüzün gösterdiği statik bir yönteme iletin. Normalde,FrameworkInitializer
sınıfınıza bunu alan statik bir@SystemApi(client = MODULE_LIBRARIES)
API eklersiniz.
Bu kalıp, diğer ana hat modüllerinin bu API'lere erişmesini engeller. Bunun nedeni, get()
ve register()
API'leri diğer modüller tarafından görülebilmesine rağmen diğer modüllerin <YourModule>ServiceManager
örneğini almasının mümkün olmamasıdır.
Telefon, telefon hizmetine referans olarak şunu kullanır: kod arama bağlantısı.
Yerel kodda bir hizmet bağlayıcı nesnesi uygularsanız AServiceManager
doğal API'lerini kullanırsınız.
Bu API'ler ServiceManager
Java API'lerine karşılık gelir ancak yerel API'ler doğrudan ana hat modüllerine sunulur. Bu işlevleri, modülünüzün sahibi olmadığı bağlayıcı nesneleri kaydetmek veya bunlara referans vermek için kullanmayın. Yerel bir bağlayıcı nesnesi gösterirseniz <YourModule>ServiceManager.ServiceRegisterer
'nizin register()
yöntemine ihtiyacı yoktur.
Ana hat modüllerindeki izin tanımları
APK içeren ana hat modülleri, normal bir APK ile aynı şekilde APK'larında AndroidManifest.xml
(özel) izinler tanımlayabilir.
Tanımlanan izin yalnızca bir modül içinde dahili olarak kullanılıyorsa izin adının önüne APK paket adı eklenmelidir. Örneğin:
<permission
android:name="com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER"
android:protectionLevel="signature" />
Tanımlanan izin, güncellenebilir bir platform API'si kapsamında diğer uygulamalara sağlanacaksa izin adının önüne "android.permission." eklenmelidir. (herhangi bir statik platform izni gibi) ve modül paket adı, adlandırma çakışmalarını önlerken bir modülden gelen platform API'si olduğunu belirtmek için kullanılır. Örneğin:
<permission
android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"
android:label="@string/active_calories_burned_read_content_description"
android:protectionLevel="dangerous"
android:permissionGroup="android.permission-group.HEALTH" />
Ardından modül, bu izin adını API yüzeyinde API sabit değeri olarak gösterebilir (ör. HealthPermissions.READ_ACTIVE_CALORIES_BURNED
).