Bu sayfa, API Konseyi'nin API incelemelerinde uyguladığı genel ilkeleri anlamaları için geliştiricilere yönelik bir rehber olarak hazırlanmıştır.
Geliştiriciler, API yazarken bu yönergeleri uygulamanın yanı sıra API Lint aracını da çalıştırmalıdır. Bu araç, bu kuralların birçoğunu API'lere karşı çalıştırdığı kontrollerde kodlar.
Bu, söz konusu Lint aracı tarafından uyulan kuralların rehberi ve bu araca yüksek doğrulukla kodlanamayan kurallar hakkında genel tavsiyeler olarak düşünülebilir.
API Lint aracı
API Lint, Metalava statik analiz aracına entegre edilmiştir ve CI'daki doğrulama sırasında otomatik olarak çalışır. m
checkapi kullanarak yerel bir platform ödeme sayfasından veya ./gradlew :path:to:project:checkApi kullanarak yerel bir AndroidX ödeme sayfasından manuel olarak çalıştırabilirsiniz.
API kuralları
Android platformu ve birçok Jetpack kitaplığı, bu yönergeler oluşturulmadan önce vardı. Bu sayfada daha sonra belirtilen politikalar, Android ekosisteminin ihtiyaçlarını karşılamak için sürekli olarak geliştirilmektedir.
Bu nedenle, mevcut bazı API'ler kurallara uymayabilir. Diğer durumlarda ise yeni bir API'nin yönergelere sıkı sıkıya uymak yerine mevcut API'lerle tutarlı kalması, uygulama geliştiricilere daha iyi bir kullanıcı deneyimi sunabilir.
Bir API ile ilgili çözülmesi gereken zor sorular veya güncellenmesi gereken yönergeler varsa kendi değerlendirmenizi yapın ve API Konseyi'ne ulaşın.
API temel bilgileri
Bu kategori, Android API'sinin temel yönleriyle ilgilidir.
Tüm API'ler uygulanmalıdır
Bir API'nin kitlesi (ör. herkese açık veya @SystemApi) ne olursa olsun, birleştirildiğinde veya API olarak kullanıma sunulduğunda tüm API yüzeyleri uygulanmalıdır. API saplarını daha sonra eklenecek uygulama ile birleştirmeyin.
Uygulamaları olmayan API yüzeylerinde birden fazla sorun vardır:
- Yüzeyin düzgün veya eksiksiz bir şekilde gösterildiği garanti edilmez. Bir API, istemciler tarafından test edilene veya kullanılana kadar istemcinin özelliği kullanabilmek için uygun API'lere sahip olup olmadığını doğrulamanın bir yolu yoktur.
- Uygulaması olmayan API'ler, geliştirici önizlemelerinde 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üzeylerinin test edilmesi, API yüzeyinin kullanılabilir olduğuna ve beklenen kullanım alanlarının ele alındığına dair temel bir garanti sağlar. Varlık testi yeterli değildir. API'nin kendisinin davranışı test edilmelidir.
Yeni bir API ekleyen 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 belgelendirilmelidir.
Belgeleme, API kullanılabilirliğinin önemli bir parçasıdır. API yüzeyinin söz dizimi açık görünse de yeni istemciler, API'nin arkasındaki anlamsal yapıyı, davranışı 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ış kodlarla aynı API yönergelerine uymalıdır.
API oluşturmak için kullanılması önerilmeyen araçlar:
AutoValue: Çeşitli şekillerde yönergeleri ihlal eder. Örneğin, AutoValue'nun çalışma şekliyle nihai değer sınıfları veya nihai oluşturucular uygulanamaz.
Kod stili
Bu kategori, geliştiricilerin kullanması gereken genel kod stiliyle ilgilidir. Özellikle herkese açık API'ler yazarken bu stil kullanılmalıdır.
Belirtilen yerler hariç standart kodlama kurallarına uyun.
Android kodlama kuralları, harici katkıda bulunanlar için burada belgelenmiştir:
https://source.android.com/source/code-style.html
Genel olarak, standart Java ve Kotlin kodlama kurallarını uygularız.
Kısaltmalar, yöntem adlarında büyük harfle yazılmamalıdır.
Örneğin: Yöntem adı runCtsTests olmalı, runCTSTests olmamalıdır.
Adlar Impl ile bitmemelidir.
Bu, uygulama ayrıntılarını ortaya çıkarır. Bundan kaçının.
Sınıflar
Bu bölümde sınıflar, arayüzler ve devralma ile ilgili kurallar açıklanmaktadır.
Yeni genel sınıfları uygun temel sınıftan devralma
Devralma, alt sınıfınızdaki uygun olmayan API öğelerini kullanıma sunar.
Örneğin, FrameLayout öğesinin yeni bir herkese açık alt sınıfı, FrameLayout
artı yeni davranışlar ve API öğeleri gibi görünür. Devralınan API kullanım alanınız için uygun değilse ağaçta daha yukarıdaki bir sınıftan devralın. Örneğin, ViewGroup veya View.
Temel sınıftaki yöntemleri geçersiz kılarak UnsupportedOperationException oluşturmak istiyorsanız hangi temel sınıfı kullandığınızı yeniden gözden geçirin.
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 her zaman temel sınıfı belirli uygulamaya tercih edin (ör. ArrayList<Foo> yerine List<Foo> döndürün).
API için uygun kısıtlamaları ifade eden bir temel sınıf kullanın. Örneğin, koleksiyonunun sıralı olması gereken bir API için List, koleksiyonunun benzersiz öğelerden oluşması gereken bir API için ise Set kullanın.
Kotlin'de değişmez koleksiyonları tercih edin. Daha fazla bilgi için Koleksiyonun değiştirilebilirliğ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 durum bilgisiz 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 uygulamada oluşturucu veya dahili durumun gerekli olduğu durumlarda, 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 öğeyi 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 sonekler
Yardımcı yöntem koleksiyonları için Helper ve Util gibi genel sınıf adı sonekleri 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ı kapsadığı durumlarda, kapsayan sınıfa ne yaptığını açıklayan anlamlı bir ad verin.
Çok sınırlı durumlarda Helper sonekini kullanmak uygun olabilir:
- Varsayılan davranışın oluşturulması için kullanılır.
- Mevcut davranışın yeni sınıflara devredilmesini içerebilir.
- Kalıcı durum gerekebilir
- Genellikle
Viewiçerir.
Örneğin, geri bağlama ipuçları için View ile ilişkili durumun kalıcı olması ve geri bağlamayı 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 olarak kullanmayın
IDL tarafından oluşturulan kodu uygulama ayrıntıları olarak tutun. Buna protobuf, soketler, FlatBuffers veya Java ve NDK dışındaki diğer API yüzeyleri dahildir. Ancak Android'deki IDL'lerin çoğu AIDL'dedir. Bu nedenle bu sayfada AIDL'ye odaklanılmıştır.
Oluşturulan AIDL sınıfları, API stil kılavuzu koşullarını karşılamaz (örneğin, aşırı yükleme kullanamazlar) ve AIDL aracı, dil API'si uyumluluğunu korumak için açıkça tasarlanmamıştır. Bu nedenle, bunları herkese açık bir API'ye yerleştiremezsiniz.
Bunun yerine, başlangıçta sığ bir sarmalayıcı olsa bile AIDL arayüzünün üzerine herkese açık bir API katmanı ekleyin.
Ciltleyici arayüzleri
Binder arayüzü bir uygulama ayrıntısıysa gelecekte serbestçe değiştirilebilir. Herkese açık katman, gerekli geriye dönük uyumluluğun korunmasına olanak tanır. Örneğin, dahili çağrılara yeni bağımsız değişkenler eklemeniz veya toplu işleme ya da akış kullanarak, paylaşılan bellek kullanarak veya benzer yöntemlerle 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 öğesini doğrudan genel API olarak kullanıma sunmayın:
// BAD: Public API generated from IFooService.aidl
public class IFooService {
public void doFoo(String foo);
}
Bunun yerine, Binder arayüzünü bir yönetici veya başka bir sınıfın içine yerleştirin:
/**
* @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şken gerekirse dahili arayüz en az düzeyde olabilir ve genel API'ye uygun aşırı yüklemeler eklenebilir. Uygulama geliştikçe diğer geriye dönük uyumluluk sorunlarını gidermek için sarmalama katmanını 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 parçası olmayan Binder arayüzler (ör. Google Play Hizmetleri tarafından uygulamaların kullanması için dışa aktarılan bir hizmet arayüzü) için kararlı, yayınlanmış ve sürüm oluşturulmuş bir IPC arayüzü şartı, arayüzün kendisini geliştirmeyi çok daha zor hale getirir. Ancak, diğer API yönergelerine uymak ve gerekirse IPC arayüzünün yeni bir sürümü için aynı genel API'yi kullanmayı kolaylaştırmak amacıyla etrafında bir sarmalayıcı katman bulundurmak yine de faydalıdır.
Herkese açık API'de ham Binder nesneleri kullanmayın
Binder nesnesi kendi başına bir anlam ifade etmediğinden genel API'de kullanılmamalıdır. Yaygın bir kullanım alanı, kimlik semantiğine sahip olduğu için Binder veya IBinder değerini jeton olarak kullanmaktır. Ham Binder nesnesi kullanmak yerine 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 beyan edilmelidir. Yönetici sınıfları, sistem hizmetleriyle iletişim kurar ve tek etkileşim noktasıdır. Özelleştirmeye gerek olmadığından final olarak beyan edin.
CompletableFuture veya Future kullanmayın
java.util.concurrent.CompletableFuture, gelecekteki değerin rastgele değiştirilmesine izin veren geniş bir API yüzeyine ve hataya açık varsayılanlara sahiptir.
Buna karşılık, java.util.concurrent.Future'da engellemeyen dinleme özelliği eksik olduğundan zaman uyumsuz kodla kullanılması zordur.
Platform kodunda ve hem Kotlin hem de Java tarafından kullanılan düşük seviyeli kitaplık API'lerinde, tamamlama geri çağırma işlevi (Executor) ve API iptali destekliyorsa CancellationSignal kullanın.
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'yı kullanabilirsiniz
ListenableFuture.
public com.google.common.util.concurrent.ListenableFuture<Foo> asyncLoadFoo();
İsteğe bağlı kullanmayın
Optional bazı API yüzeylerinde avantajlı olsa da mevcut Android API yüzey alanıyla tutarsızdır. @Nullable ve @NonNull, null güvenliği için araç yardımı sağlar. Kotlin ise derleyici düzeyinde nullability sözleşmelerini zorunlu kılar ve Optional'ı gereksiz hâle getirir.
İsteğe bağlı temel öğeler için eşlenmiş has ve get yöntemlerini kullanın. Değer ayarlanmamışsa (has, false döndürürse) get yöntemi IllegalStateException oluşturmalıdır.
public boolean hasAzimuth() { ... }
public int getAzimuth() {
if (!hasAzimuth()) {
throw new IllegalStateException("azimuth is not set");
}
return azimuth;
}
Örneklenemeyen sınıflar için özel oluşturucular 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 örneklendirilemeyen sınıflar, varsayılan bağımsız değişken içermeyen oluşturucu kullanılarak örneklendirmeyi önlemek için en az bir özel oluşturucu içermelidir.
public final class Log {
// Not instantiable.
private Log() {}
}
Singleton'lar
Singleton'lar, testle ilgili şu dezavantajları nedeniyle önerilmez:
- Yapı, sınıf tarafından yönetilir ve sahtelerin kullanılmasını önler.
- Singleton'ın statik yapısı nedeniyle testler hermetik olamaz.
- Geliştiriciler bu sorunları gidermek için tekil nesnenin dahili ayrıntılarını bilmeli veya tekil nesnenin etrafında bir sarmalayıcı oluşturmalıdır.
Bu sorunları gidermek için soyut bir temel sınıfa dayanan tek örnek modelini tercih edin.
Tek örnek
Tek örnekli sınıflar, private veya internal oluşturucuya sahip soyut bir temel sınıf kullanır ve bir ö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, singleton'dan farklı olarak geliştiricilerin SingleInstance'nin sahte bir sürümünü oluşturup sarmalayıcı oluşturmak zorunda kalmadan uygulamayı yönetmek için kendi bağımlılık yerleştirme çerçevelerini kullanabilmesi veya kitaplığın -testing yapısında kendi sahtesini sağlayabilmesi açısından farklıdır.
Kaynakları serbest bırakan sınıflar AutoCloseable'ı uygulamalıdır.
close, release, destroy veya benzeri yöntemlerle kaynakları serbest bırakan 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 yöntemini uygulamalıdır.
android.* içinde yeni View alt sınıfları kullanmaktan kaçının.
Platform herkese açık API'sinde (yani android.* içinde) doğrudan veya dolaylı olarak android.view.View öğesinden devralan yeni sınıflar oluşturmayın.
Android'in kullanıcı arayüzü araç seti artık Compose-first. Platform tarafından kullanıma sunulan yeni kullanıcı arayüzü özellikleri, Jetpack Compose'u ve isteğe bağlı olarak Jetpack kitaplıklarındaki geliştiriciler için görünüm tabanlı kullanıcı arayüzü bileşenlerini uygulamak üzere kullanılabilecek daha düşük düzeyli API'ler olarak kullanıma sunulmalıdır. Bu bileşenlerin kitaplıklarda sunulması, platform özellikleri kullanılamadığında geriye dönük bağlantı uygulamaları için fırsatlar sunar.
Alanlar
Bu kurallar, sınıflardaki herkese açık alanlarla ilgilidir.
İşlenmemiş alanları kullanıma sunmayın
Java sınıfları, alanları doğrudan kullanıma sunmamalıdır. Alanlar özel olmalı ve bu alanların nihai olup olmadığına bakılmaksızın yalnızca herkese açık alıcılar ve ayarlayıcılar kullanılarak erişilebilir olmalıdır.
Alan belirtme veya alma davranışının geliştirilmesine gerek olmayan temel veri yapıları bu nadir istisnalar arasındadır. Bu gibi durumlarda alanlar, standart değişken adlandırma kuralları kullanılarak adlandırılmalıdır. Örneğin, Point.x ve Point.y.
Kotlin sınıfları özellikleri kullanıma sunabilir.
Herkese açık alanlar nihai olarak işaretlenmelidir.
Ham alanların kullanılması kesinlikle önerilmez (@see
Don't expose raw fields). Ancak nadir durumlarda bir alan herkese açık alan olarak kullanılıyorsa bu alanı final olarak işaretleyin.
Dahili alanlar gösterilmemelidir.
Herkese açık API'de dahili alan adlarına referans vermeyin.
public int mFlags;
Korunan yerine herkese açık kullanın
@see Use public instead of protected
Sabitler
Bunlar, herkese açık sabitlerle ilgili kurallardır.
İşaret sabitleri, int veya uzun değerlerle çakışmamalıdır.
İşaretler, bazı birleşim değerlerinde birleştirilebilen bitleri ifade eder. Bu durum söz konusu değilse değişkeni veya sabiti flag olarak adlandı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 bitmask işaretlerine bakın.
static final sabitleri, tamamı büyük harflerden oluşan ve alt çizgiyle ayrılmış bir adlandırma kuralı kullanmalıdır.
Sabitteki 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 önekler kullanın
Android'de kullanılan sabitlerin çoğu; işaretler, anahtarlar ve işlemler gibi standart şeyler içindir. Bu sabitler, daha kolay tanımlanabilmeleri için standart ön eklere sahip olmalıdır.
Örneğin, amaç 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.
Önemli sabit adları ve kapsamları
Dize sabit değerleri, sabit adın kendisiyle tutarlı olmalı ve genellikle paket veya alanla sınırlandırılmalıdır. Örneğin:
public static final String FOO_THING = "foo"
ne tutarlı bir şekilde adlandırılmış ne de uygun şekilde kapsamlandırılmıştır. Bunun yerine şunları deneyin:
public static final String FOO_THING = "android.fooservice.FOO_THING"
Kapsamlı dize sabitlerindeki android önekleri 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 adlandırılmalıdır.
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"
}
Korunan yerine herkese açık kullanın
@see Use public instead of protected
Tutarlı önekler kullanma
İlgili sabitlerin tümü aynı önekle başlamalıdır. Örneğin, işaret değerleriyle kullanılacak bir sabitler 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 Use standard prefixes for constants
Tutarlı kaynak adları kullanma
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_recentsComponentNamegibi platform yapılandırma değerleri - attrs.xml dosyasındaki
@attr/layout_marginStartgibi düzene özgü görünüm özellikleri
Herkese açık temalar ve stiller, hiyerarşik PascalCase adlandırma kuralına uymalıdır. Örneğin, Java'daki iç içe sınıflara benzer şekilde @style/Theme.Material.Light.DarkActionBar veya @style/Widget.Material.SearchView.ActionBar.
Düzen ve çizilebilir kaynaklar herkese açık API'ler olarak sunulmamalıdır. Ancak bu öğelerin kullanıma sunulması gerekiyorsa herkese açık düzenler ve çizilebilir öğeler, under_score adlandırma kuralı kullanılarak adlandırılmalıdır. Örneğin, layout/simple_list_item_1.xml veya drawable/title_bar_tall.xml.
Sabitlerin değişebileceği durumlarda bunları dinamik hale getirin.
Derleyici, sabit değerleri satır içi olarak ekleyebilir. Bu nedenle, değerlerin aynı kalması API sözleşmesinin bir parçası olarak kabul edilir. MIN_FOO veya MAX_FOO
sabitinin değeri gelecekte değişebilirse bunları dinamik yöntemler
olarak kullanmayı düşünebilirsiniz.
CameraManager.MAX_CAMERAS
CameraManager.getMaxCameras()
Geri aramalar için ileri 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 sunulan sabitler, uygulamanın hedef API sürümünü dikkate almalı ve daha yeni sabitleri tutarlı bir değerle eşlemelidir. Aşağıdaki senaryoyu ele alalım:
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 tasarlanmış ve yalnızca iki olası durum olduğu yönünde (biraz) makul bir varsayımda bulunmuştur. Ancak uygulama, yeni eklenen STATUS_FAILURE_RETRY öğesini aldığında bunu başarı olarak yorumlar.
Sabitleri döndüren yöntemler, çıkışlarını uygulamanın hedeflediği API düzeyine uyacak şekilde kısıtlayarak bu tür durumları güvenli bir şekilde işleyebilir:
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, sabitler 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 varsayar. Bu beklentiyi karşılamak istemiyorsanız API'niz için genel bir sabit kullanmanın iyi bir fikir olup olmadığını yeniden değerlendirin.
Ayrıca kitaplıklar, uygulamadan ayrı kendi targetSdkVersion değerlerini belirleyemez ve kitaplık kodundan targetSdkVersion davranış değişikliklerini işlemek karmaşık ve hataya açık bir süreçtir.
Tam sayı veya dize sabiti
Değerler için ad alanı, paketinizin dışında genişletilemiyorsa tam sayı sabitleri ve @IntDef kullanın. Ad alanı paylaşılıyorsa veya paketinizin dışındaki kodlarla genişletilebiliyorsa dize sabitlerini kullanın.
Veri sınıfları
Veri sınıfları, değişmez özellikler kümesini temsil eder ve bu verilerle etkileşim kurmak için küçük ve iyi tanımlanmış bir yardımcı işlevler kümesi sağlar.
Kotlin derleyicisi, oluşturulan kod için dil API'si veya ikili uyumluluk garantisi vermediğinden herkese açık Kotlin API'lerinde data class kullanmayın. Bunun yerine, gerekli işlevleri manuel olarak uygulayın.
Örneklendirme
Java'da, veri sınıfları az sayıda özellik olduğunda oluşturucu sağlamalı veya çok sayıda özellik olduğunda Builder kalıbını kullanmalıdır.
Kotlin'de veri sınıfları, özelliklerin sayısından bağımsız olarak varsayılan bağımsız değişkenlere sahip bir oluşturucu sağlamalıdır. Kotlin'de tanımlanan veri sınıfları, Java istemcileri hedeflenirken oluşturucu sağlamaktan da yararlanabilir.
Değiştirme ve kopyalama
Verilerin değiştirilmesi gerektiği durumlarda, yeni bir nesne döndüren bir kopya oluşturucuya (Java) sahip bir Builder sınıfı veya 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 oluşturucusuyla eşleşmeli ve varsayılanlar 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() yöntemlerini uygulamalıdır ve her özellik bu yöntemlerin uygulamalarında hesaba katılmalıdır.
Veri sınıfları, toString() öğesini Kotlin'in veri sınıfı uygulamasıyla eşleşen önerilen bir biçimde (ör. User(var1=Alex, var2=42)) uygulayabilir.
Yöntemler
Bunlar, yöntemlerdeki çeşitli ayrıntılar, parametreler, yöntem adları, dönüş türleri ve erişim belirleyicilerle ilgili kurallardır.
Süre
Bu kurallar, API'lerde tarih ve süre gibi zaman kavramlarının 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ü, desugaring aracılığıyla tüm platform sürümlerinde kullanılabilir ve API parametrelerinde veya dönüş değerlerinde zamanı ifade ederken tercih edilmelidir.
API alanının, amaçlanan kullanım kalıplarında nesne ayırmanın yasaklayıcı bir performans etkisi yaratacağı bir alan olmadığı sürece, yalnızca java.time.Duration veya java.time.Instant kabul eden ya da döndüren API varyantlarını kullanmayı tercih edin ve aynı kullanım alanına sahip temel varyantları atlayın.
Süreleri ifade eden yöntemler, duration olarak adlandırılmalıdır.
Bir zaman değeri, söz konusu 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çerliyse "timeout" (zaman aşımı) uygundur.
java.time.Instant türündeki "time" özelliği, bir süreye değil, belirli bir zamana atıfta bulunurken uygundur.
Süreleri veya zamanı temel öğe olarak ifade eden yöntemler, zaman birimleriyle adlandırılmalı ve uzun değerler kullanılmalıdır.
Süreleri temel tür olarak kabul eden veya döndüren yöntemler, java.time.Duration ile kullanılmak üzere süslenmemiş adı ayırmak için yöntem adına ilişkili zaman birimlerini (ör. Millis, Nanos, Seconds) eklemelidir. Zaman başlıklı makaleyi inceleyin.
Yöntemler, birim ve zaman tabanlarıyla da uygun şekilde açıklanmalıdır:
@CurrentTimeMillisLong: Değer, 1970-01-01T00:00:00Z tarihinden itibaren milisaniye sayısı olarak ölçülen negatif olmayan bir zaman damgasıdır.@CurrentTimeSecondsLong: Değer, 1970-01-01T00:00:00Z tarihinden itibaren geçen saniye sayısı olarak ölçülen negatif olmayan bir zaman damgasıdır.@DurationMillisLong: Değer, milisaniye cinsinden negatif olmayan bir süredir.@ElapsedRealtimeLong: Değer,SystemClock.elapsedRealtime()zaman tabanında negatif olmayan bir zaman damgasıdır.@UptimeMillisLong: Değer,SystemClock.uptimeMillis()zaman tabanında negatif olmayan bir zaman damgasıdır.
Temel zaman parametreleri veya dönüş değerleri int değil, long kullanmalıdır.
ValueAnimator.setDuration(@DurationMillisLong long);
ValueAnimator.setDurationNanos(long);
Zaman birimlerini ifade eden yöntemler, birim adları için kısaltılmamış kısa biçimi tercih etmelidir.
public void setIntervalNs(long intervalNs);
public void setTimeoutUs(long timeoutUs);
public void setIntervalNanos(long intervalNanos);
public void setTimeoutMicros(long timeoutMicros);
Uzun süreli bağımsız değişkenlere açıklama ekleme
Platform, long türü zaman birimleri için daha güçlü yazım sağlamak üzere çeşitli ek açıklamalar içerir:
@CurrentTimeMillisLong: Değer,1970-01-01T00:00:00Ztarihinden itibaren milisaniye sayısı olarak ölçülen negatif olmayan bir zaman damgasıdır. Bu nedenle,System.currentTimeMillis()zaman tabanındadır.@CurrentTimeSecondsLong: Değer,1970-01-01T00:00:00Ztarihinden itibaren geçen saniye sayısı olarak ölçülen negatif olmayan bir zaman damgasıdır.@DurationMillisLong: Değer, milisaniye cinsinden negatif olmayan bir süredir.@ElapsedRealtimeLong: Değer,SystemClock#elapsedRealtime()zaman tabanında negatif olmayan bir zaman damgasıdır.@UptimeMillisLong: Değer,SystemClock#uptimeMillis()zaman tabanında negatif olmayan bir zaman damgasıdır.
Ölçü birimleri
Zaman dışında bir ölçü birimini ifade eden tüm yöntemler için CamelCased SI birim önekleri tercih edin.
public long[] getFrequenciesKhz();
public float getStreamVolumeDb();
İsteğe bağlı parametreleri aşırı yüklemelerin sonuna yerleştirin.
İsteğe bağlı parametreler içeren bir yöntemin aşırı yüklemeleri varsa bu parametreleri sonda tutun ve diğer parametrelerle tutarlı bir sıralama kullanı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üklemeler eklerken daha basit yöntemlerin davranışı, varsayılan bağımsız değişkenler daha ayrıntılı yöntemlere 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ürlerde bağımsız değişkenler kabul etmek dışında yöntemleri aşırı yüklemeyin. Aşırı yüklenmiş yöntem temelde farklı bir şey yapıyorsa ona yeni bir ad verin.
Varsayılan parametreleri olan yöntemler @JvmOverloads ile açıklama eklenmelidir (yalnızca Kotlin).
İkili uyumluluğu korumak için varsayılan parametreleri olan yöntemler ve oluşturucular @JvmOverloads ile açıklama eklenmelidir.
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 gönderilmişse varsayılan değerin kaldırılması kaynakta değişiklik yapılmasına neden olur.
En belirgin ve tanımlayıcı yöntem parametreleri önce gelmelidir.
Birden fazla parametresi olan bir yönteminiz varsa en alakalı olanları önce yerleştirin. İşlem yapılan nesneyi açıklayan parametreler, işaretleri ve diğer seçenekleri belirten parametrelerden daha önemlidir. Tamamlama geri araması varsa bunu en sona yerleştirin.
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: Aşırı yüklemelerde isteğe bağlı parametreleri sona yerleştirme
Yapımcılar
Builder kalıbı, karmaşık Java nesneleri oluşturmak için önerilir ve Android'de şu durumlarda en çok tercih edilen yöntemdir:
- Elde edilen nesnenin özellikleri sabit olmalıdır.
- Çok sayıda gerekli özellik vardır. Örneğin, birçok oluşturucu bağımsız değişkeni
- Oluşturma sırasında mülkler arasında karmaşık bir ilişki vardır. Örneğin, doğrulama adımı gereklidir. Bu karmaşıklık düzeyinin genellikle API'nin kullanılabilirliğiyle ilgili sorunlara işaret ettiğini unutmayın.
Oluşturucuya ihtiyacınız olup olmadığını değerlendirin. Oluşturucular, aşağıdaki amaçlarla kullanıldığında API yüzeyinde faydalıdır:
- İsteğe bağlı oluşturma parametrelerinin olası büyük bir kümesinden yalnızca birkaçını yapılandırma
- Çok sayıda farklı isteğe bağlı veya zorunlu oluşturma parametresini yapılandırın. Bazen benzer veya eşleşen türlerdeki bu parametreler, aksi takdirde arama sitelerinin okunmasını zorlaştırabilir veya yazarken hata yapma olasılığını artırabilir.
- Bir nesnenin artımlı olarak oluşturulmasını yapılandırın. Burada, farklı yapılandırma kodu parçalarının her biri, uygulama ayrıntıları olarak oluşturucuya çağrı 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 sayıda zorunlu parametreye sahip ve isteğe bağlı parametre içermeyen bir türünüz varsa neredeyse her zaman oluşturucuyu atlayıp basit bir oluşturucu kullanabilirsiniz.
Kotlin kaynaklı sınıflar, oluşturucular yerine varsayılan bağımsız değişkenlere sahip @JvmOverloads ek açıklamalı oluşturucuları tercih etmelidir ancak daha önce belirtilen durumlarda oluşturucular da sağlayarak Java istemcilerinin kullanılabilirliğini 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.
Oluşturucu sınıfları, build() dışındaki her yöntemde Oluşturucu nesnesini (ör. this) döndürerek yöntem zincirlemeyi 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();
}
}
Temel bir oluşturucu sınıfının genişletmeyi desteklemesi gereken nadir durumlarda genel bir dönüş 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ı, oluşturucu aracılığıyla oluşturulmalıdır.
Android API yüzeyinde tutarlı oluşturucu oluşturma işlemini sürdürmek için tüm oluşturucular mutlaka statik oluşturucu yöntemiyle değil, oluşturucu üzerinden oluşturulmalıdır. Kotlin tabanlı API'lerde, Kotlin kullanıcılarının bir fabrika yöntemi/DSL tarzı oluşturma mekanizması aracılığıyla oluşturucuya örtülü olarak güvenmesi beklense bile Builder herkese açık olmalıdır. Kitaplıklar, Builder sınıf oluşturucusunu Kotlin istemcilerinden seçerek gizlemek için @PublishedApi internal 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 yönelik tüm bağımsız değişkenler zorunlu olmalıdır (ör. @NonNull).
İsteğe bağlı olarak, örneğin @Nullable, bağımsız değişkenler ayarlayıcı yöntemlere taşınmalıdır.
Oluşturucu oluşturucu, gerekli bağımsız değişkenler belirtilmemişse NullPointerException oluşturmalıdır (Objects.requireNonNull kullanmayı düşünebilirsiniz).
Oluşturucu sınıfları, oluşturulan türlerinin nihai statik iç sınıfları olmalıdır.
Bir pakette mantıksal düzen sağlamak için oluşturucu sınıfları genellikle oluşturulan türlerinin nihai iç sınıfları olarak gösterilmelidir. Örneğin, Tone.Builder yerine ToneBuilder.
Oluşturucular, mevcut bir örnekten yeni bir örnek oluşturmak için bir oluşturucu içerebilir.
Oluşturucular, mevcut bir oluşturucudan veya oluşturulmuş nesneden yeni bir oluşturucu örneği oluşturmak için bir kopya oluşturucu içerebilir. Mevcut derleyicilerden veya derleme nesnelerinden derleyici örnekleri oluşturmak için alternatif yöntemler sunmamalıdırlar.
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şkenler almalıdır.
Yeni bir oluşturucu örneği mevcut bir örnekten oluşturulabilecekse sıfırlama işlemi gereklidir. Kopya oluşturucu yoksa oluşturucuda @Nullable veya @NonNullable bağımsız değişkenleri 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şkenler alabilir.
Özellikle oluşturucular ve aşırı yüklemeler yerine varsayılan bağımsız değişkenleri kullanan Kotlin'de ikinci derece giriş için boş değer atanabilir bir değer kullanmak genellikle daha basittir.
Ayrıca, @Nullable ayarlayıcılar bunları alıcılarıyla eşleştirir. İsteğe bağlı özellikler için alıcılar @Nullable olmalıdır.
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 (setter çağrılmamışsa) ve null'nın anlamı hem setter hem de getter'da düzgün şekilde belgelenmelidir.
/**
* ...
*
* <p>Defaults to {@code null}, which means the optional value won't be used.
*/
Oluşturucu ayarlayıcılar, oluşturulan sınıfta ayarlayıcıların bulunduğu değiştirilebilir özellikler için sağlanabilir.
Sınıfınızın değiştirilebilir özellikleri varsa ve Builder sınıfına ihtiyacı varsa öncelikle sınıfınızın gerçekten değiştirilebilir özelliklere sahip olup olmaması gerektiğini kendinize sorun.
Ardından, değiştirilebilir özelliklere ihtiyacınız olduğundan eminseniz aşağıdaki senaryolardan hangisinin beklenen kullanım alanınıza daha uygun olduğuna karar verin:
Oluşturulan nesne hemen kullanılabilir olmalıdır. Bu nedenle, değiştirilebilir veya değiştirilemez olsun, tüm ilgili ö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 kullanılabilmesi için bazı ek çağrıların 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 get metotları olmamalıdır
Getter, oluşturucu üzerinde değil, oluşturulan nesne üzerinde olmalıdır.
Oluşturucu ayarlayıcıları, oluşturulan sınıfta karşılık gelen alıcılara sahip 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();
}
Builder yöntemi adlandırma
Oluşturucu yöntem adları setFoo(), addFoo() veya clearFoo() stilini kullanmalıdır.
Oluşturucu sınıflarının bir build() yöntemi bildirmesi beklenir.
Oluşturucu sınıfları, oluşturulan nesnenin bir örneğini döndüren bir build() yöntemi bildirmelidir.
Oluşturucu 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. Nesne, geçersiz parametreler nedeniyle oluşturulamıyorsa doğrulama oluşturma yöntemine ertelenebilir ve IllegalStateException istisnası oluşturulmalıdır.
Dahili kilitleri kullanıma sunmayın
Herkese açık API'deki yöntemlerde synchronized anahtar kelimesi kullanılmamalıdır. Bu anahtar kelime, nesnenizin veya sınıfınızın kilit olarak kullanılmasına neden olur ve diğerlerine açık olduğundan, sınıfınızın dışındaki başka bir kod 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 tarzı yöntemler, Kotlin özelliği yönergelerine uymalıdır.
Kotlin kaynaklarından görüntülendiğinde, erişimci tarzı yöntemler (get, set veya is öneklerini kullananlar) Kotlin özellikleri olarak da kullanılabilir.
Örneğin, Java'da tanımlanan int getField(), Kotlin'de val field: Int özelliği 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 önekleri kullanan yöntemler Java alanlarına benzer şekilde davranmalıdır. Aşağıdaki durumlarda erişimci tarzı önekler 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 maliyetli bir çalışma içeriyor.
computetercih edin. - Yöntem, bir değer döndürmek için engellemeyi veya başka bir şekilde uzun süren çalışmayı içerir (ör. IPC veya diğer G/Ç).
fetchtercih edilir. - Yöntem, bir değer döndürene kadar iş parçacığını engeller.
awaittercih edilir. - Yöntem, her çağrıda yeni bir nesne örneği döndürür.
createtercih edilir. - Yöntem başarıyla değer döndürmeyebilir.
requesttercih edilir.
Hesaplama açısından maliyetli bir işi bir kez yapıp değeri sonraki çağrılar için önbelleğe almanın, hesaplama açısından maliyetli bir işi yapma olarak değerlendirileceğini unutmayın. Jank, kareler arasında amortize edilmez.
Boole erişim yöntemleri için "is" ön ekini kullanma
Bu, Java'daki boolean yöntemleri ve alanları için standart adlandırma kuralıdır. Genel olarak, boole yöntemi ve değişken adları, dönüş değeriyle yanıtlanan sorular olarak yazılmalıdır.
Java boolean erişimci yöntemleri set/is adlandırma şemasını izlemeli ve alanlar is'ı 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şimci yöntemleri için set/is veya Java alanları için is kullanılması, bunların Kotlin'de özellik olarak kullanılmasını sağlar:
obj.isVisible = true
obj.isFactoryResetProtectionEnabled = false
if (!obj.isAvailable) return
Özellikler ve erişimci yöntemleri genellikle olumlu adlandırma kullanmalıdır. Örneğin, Disabled yerine Enabled. Olumsuz terminoloji kullanmak true ve false anlamını tersine çevirir ve davranış hakkında 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 dizimiyle ç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 önekler arasında can ve should yer alır:
// "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 etkinleştiren ya da devre dışı bırakan yöntemler is önekini ve Enabled sonekini kullanabilir:
// "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ığı gösteren yöntemler is önekini ve Supported (Destekleniyor) veya Required (Gerekli) sonekini kullanabilir:
// "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()
Genel olarak, yöntem adları, dönüş değeriyle yanıtlanan sorular şeklinde yazılmalıdır.
Kotlin özellik yöntemleri
Bir sınıf özelliği için var foo: Foo Kotlin, tutarlı bir kural kullanarak get/set yöntemleri oluşturur: getter için get ekleyin ve ilk karakteri büyük harfle yazın, setter için set ekleyin ve ilk karakteri büyük harfle yazın. Özellik bildirimi sırasıyla public Foo getFoo() ve public void setFoo(Foo foo) adlı yöntemleri oluşturur.
Özellik Boolean türündeyse ad oluşturma işleminde ek bir kural geçerlidir: Özellik adı is ile başlıyorsa getter yöntemi adına get eklenmez, getter olarak özellik adı kullanılır.
Bu nedenle, adlandırma yönergesine uymak için Boolean özelliklerini is ön ekiyle adlandırmanız önerilir:
var isVisible: Boolean
Mülkünüz yukarıda belirtilen istisnalardan biriyse ve uygun bir önekle 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
Bitmask erişimcileri
Bitmask işaretlerini tanımlamayla ilgili API yönergeleri için Bitmask işaretleri için @IntDef kullanma başlıklı makaleyi inceleyin.
Setters
İki belirleyici yöntem 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);
Getters
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();
Korunan yerine herkese açık kullanın
Herkese açık API'de her zaman public yerine protected tercih edilir. Uygulayıcıların, varsayılan olarak harici erişimin de aynı derecede iyi olacağı durumlarda herkese açık erişimciler sağlamak için geçersiz kılma işlemi yapması gerektiğinden, korumalı erişim uzun vadede sorunlu hale gelir.
protectedGörünürlüğün geliştiricilerin bir API'yi çağırmasını engellemediğini, yalnızca biraz daha rahatsız edici hale getirdiğini unutmayın.
equals() ve hashCode() yöntemlerinden hiçbirini veya her ikisini de uygulamayın.
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ıklamasına yardımcı olmak için toString() işlevini geçersiz kılması önerilir.
Çıkışın program davranışı mı yoksa hata ayıklama için mi olduğunu belgeleyin.
Program davranışının uygulamanıza bağlı olmasını isteyip istemediğinize karar verin. Örneğin, UUID.toString() ve File.toString(), programların kullanması için kendi biçimlerini belgeler. Yalnızca hata ayıklama amacıyla Intent gibi bilgileri kullanıma sunuyorsanız üst sınıftaki dokümanları devraldığınızı belirtin.
Ek bilgi eklemeyin
toString()'dan edinilebilen tüm bilgiler, nesnenin genel API'si aracılığıyla da edinilebilmelidir. Aksi takdirde, geliştiricileri toString() çıkışınızı ayrıştırmaya ve buna güvenmeye teşvik etmiş olursunuz. Bu da gelecekteki değişiklikleri engeller. İyi bir uygulama, toString() işlevini yalnızca nesnenin genel API'sini kullanarak uygulamaktır.
Hata ayıklama çıkışına güvenmeyi engelleme
Geliştiricilerin hata ayıklama çıkışına bağlı kalmasını önlemek mümkün olmasa da nesnenizin System.identityHashCode değerini toString() çıkışına eklemek, iki farklı nesnenin eşit toString() çıkışına sahip olma olasılığını çok düşürür.
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " {mFoo=" + mFoo + "}";
}
Bu, geliştiricilerin nesnelerinizde assertThat(a.toString()).isEqualTo(b.toString()) gibi test onayları yazmasını etkili bir şekilde engelleyebilir.
Yeni oluşturulan nesneleri döndürürken createFoo'yu kullanın.
Döndürülen değerler oluşturacak yöntemler için (ör. yeni nesneler oluşturarak) get veya new yerine create önekini kullanın.
Yöntem, döndürülecek bir nesne oluşturduğunda bunu yöntem adında açıkça belirtin.
public FooThing getFooThing() {
return new FooThing();
}
public FooThing createFooThing() {
return new FooThing();
}
File 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çerik 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)
Kutuya yerleştirilmiş sürümler yerine ham temel öğeleri alın ve döndürün.
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)
Temel türlerin sınıf eşdeğerlerinden kaçınmak, bu sınıfların bellek yükünü, değerlere yöntem erişimini ve daha da önemlisi, temel türler ile nesne türleri arasında yayınlamadan kaynaklanan otomatik kutulama işlemini önler. Bu davranışlardan kaçınmak, pahalı ve daha sık çöp toplama işlemlerine yol açabilecek geçici bellek ayırmaları ve bellek kullanımından tasarruf etmenizi sağlar.
Geçerli parametreleri ve döndürülen değerleri netleştirmek için ek açıklamalar kullanın.
Çeşitli durumlarda izin verilen değerleri netleştirmek için geliştirici ek açıklamaları eklendi. Bu sayede, geliştiriciler yanlış değerler sağladığında (ör. çerçeve belirli bir sabit değerler kümesinden birini gerektirirken rastgele bir int iletme) araçların onlara yardımcı olması kolaylaşır. Uygun olduğunda aşağıdaki ek açıklamaların tümünü kullanın:
Boş değer atanabilirliği
Java API'leri için açık null değer alabilme ek açıklamaları gerekir ancak null değer alabilme kavramı Kotlin dilinin bir parçasıdır ve null değer alabilme ek açıklamaları Kotlin API'lerinde asla kullanılmamalıdır.
@Nullable: Belirli bir dönüş değerinin, parametrenin veya alanın boş (null) olabileceğini gösterir:
@Nullable
public String getName()
public void setName(@Nullable String name)
@NonNull: Belirli bir dönüş değerinin, parametrenin veya alanın boş olamayacağını gösterir. Öğeleri @Nullable olarak işaretleme, Android'de nispeten yeni bir özellik olduğundan Android'in API yöntemlerinin çoğu tutarlı bir şekilde belgelenmemiştir. Bu nedenle, "bilinmiyor, @Nullable, @NonNull" olmak üzere üç durumumuz var. Bu nedenle @NonNull, API yönergelerinin bir parçasıdır:
@NonNull
public String getName()
public void setName(@NonNull String name)
Android platform belgelerinde, yöntem parametrelerinize açıklama eklediğinizde, parametre belgesinde başka bir yerde açıkça "null" kullanılmadığı sürece "Bu değer boş olabilir" şeklinde otomatik olarak doküman oluşturulur.
Mevcut "not really nullable" yöntemleri: API'deki @Nullable ek açıklaması olmayan mevcut yöntemler, yöntem belirli ve açık koşullarda (ör. findViewById()) null döndürebiliyorsa @Nullable olarak açıklanabilir. IllegalArgumentException atan @NotNull requireFoo() yardımcı yöntemleri, null kontrolü yapmak istemeyen geliştiriciler için eklenmelidir.
Arayüz yöntemleri: Yeni API'ler, arayüz yöntemlerini uygularken uygun ek açıklamayı eklemelidir (ör. Parcelable.writeToParcel()). Yani, uygulayan sınıftaki yöntem writeToParcel(@NonNull Parcel,
int) olmalı, writeToParcel(Parcel, int) olmamalıdır. Ancak ek açıklamaları olmayan mevcut API'lerin "düzeltilmesi" gerekmez.
Boş değer atanabilirliği yaptırımı
Java'da, @NonNull parametreleri için giriş doğrulaması yapmak üzere Objects.requireNonNull() kullanılması ve parametreler null olduğunda NullPointerException oluşturulması ö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ıyla açıklama eklenmelidir.
Her kaynak türü için (ör. @StringRes, @ColorRes ve @AnimRes) bir açıklama bulunur. Ayrıca, her şeyi kapsayan @AnyRes açıklaması da vardır. Örneğin:
public void setTitle(@StringRes int resId)
Sabit kümeler için @IntDef
Sihirli sabitler: String ve int parametreleri, herkese açık sabitlerle belirtilen sınırlı bir olası değerler kümesinden birini alacak şekilde tasarlanmıştır. Bu parametreler @StringDef veya @IntDef ile uygun şekilde açıklama eklenmelidir. Bu ek açıklamalar, izin verilen parametreler için typedef gibi çalışan 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);
Açıklama eklenmiş parametrelerin geçerliliğini kontrol etmek ve parametre @IntDef'ün bir parçası değilse IllegalArgumentException hata vermek için yöntemler önerilir.
Bit maskesi işaretleri için @IntDef
Açıklamada, sabitlerin işaret olduğu da belirtilebilir ve bunlar & ve I ile birleştirilebilir:
/** @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, bir önceki bölümdeki @IntDef ile aynı olan ancak String sabitleri için kullanılan @StringDef açıklaması da vardır. Tüm değerler için otomatik olarak doküman oluşturmak üzere kullanılan birden fazla "prefix" değeri ekleyebilirsiniz.
SDK sabitleri için @SdkConstant
@SdkConstant Şu SdkConstant değerlerden biri olduğunda herkese açık alanlara açıklama ekleyin: ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION,
INTENT_CATEGORY, FEATURE.
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CALL = "android.intent.action.CALL";
Geçersiz kılmalar için uyumlu nullability sağlama
API uyumluluğu için geçersiz kılmaların boş değer atanabilirliği, üst öğenin geçerli boş değer atanabilirliğiyle uyumlu olmalıdır. Aşağıdaki tabloda uyumluluk beklentileri gösterilmektedir. Açıkça belirtmek gerekirse geçersiz kılmalar, yalnızca geçersiz kıldıkları öğe kadar veya daha kısıtlayıcı olmalıdır.
| Tür | Ebeveyn | Çocuk |
|---|---|---|
| Dönüş türü | Notlandırılmamış | Açıklama eklenmemiş veya boş olmayan |
| Dönüş türü | Null değer alabilir | Null olabilir veya null olamaz |
| Dönüş türü | Nonnull | Nonnull |
| Eğlenceli tartışma | Notlandırılmamış | Notlandırılmamış veya boş değer atanabilir |
| Eğlenceli tartışma | Null değer alabilir | Null değer alabilir |
| Eğlenceli tartışma | Nonnull | Null olabilir veya null olamaz |
Mümkün olduğunda boş değer atanamayan (ör. @NonNull) bağımsız değişkenleri tercih edin.
Yöntemler aşırı yüklendiğinde tüm bağımsız değişkenlerin null olmayan değerler olması 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 boş olmamalıdır ve özelliğin temizlenmesi ayrı bir yöntem olarak uygulanmalıdır. Bu, geliştiricinin gerekli olmamasına rağmen sondaki parametreleri ayarlaması gereken "anlamsız" ç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ılar için boş değer içermeyen (ör. @NonNull) dönüş türlerini tercih edin.
Bundle veya Collection gibi kapsayıcı türleri için boş ve geçerli olduğu 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 kullanılacak durumlarda ayrı bir boolean yöntemi sağlamayı düşünebilirsiniz.
@NonNull
public Bundle getExtras() { ... }
Get ve set çiftleri için nullability ek açıklamaları aynı olmalıdır.
Tek bir mantıksal özellik için get ve set yöntemi çiftleri, nullability ek açıklamalarında her zaman aynı olmalıdır. Bu yönergeye uyulmaması, Kotlin'in özellik söz dizimini geçersiz kılar. Bu nedenle, mevcut özellik yöntemlerine uyuşmayan nullability ek açıklamaları eklemek, Kotlin kullanıcıları için kaynak uyumluluğunu bozan bir değişikliktir.
@NonNull
public Bundle getExtras() { ... }
public void setExtras(@NonNull Bundle bundle) { ... }
Başarısızlık veya hata koşullarında döndürülen değer
Tüm API'ler, uygulamaların hatalara tepki vermesine izin vermelidir. false, -1, null veya "bir şeyler ters gitti" gibi diğer genel hata değerlerini döndürmek, geliştiriciye kullanıcı beklentilerini karşılayamama veya uygulamasının sahada güvenilirliğini doğru şekilde izleme konusundaki başarısızlık hakkında yeterli bilgi vermez. API tasarlarken uygulama oluşturduğunuzu hayal edin. Bir hatayla karşılaşırsanız API, kullanıcıya sunmak veya uygun şekilde tepki vermek için yeterli bilgiyi veriyor mu?
- Hata mesajına ayrıntılı bilgi eklemek sorun değildir (hatta teşvik edilir) ancak geliştiricilerin hatayı uygun şekilde işlemek için bu bilgileri ayrıştırması gerekmez. Ayrıntılı hata kodları veya diğer bilgiler yöntem olarak sunulmalıdır.
- Seçtiğiniz hata işleme seçeneğinin, gelecekte yeni hata türleri ekleme esnekliği sağladığından emin olun.
@IntDefiçin bu,OTHERveyaUNKNOWNdeğerinin eklenmesi anlamına gelir. Yeni bir kod döndürürken, uygulamanın bilmediği bir hata kodunu döndürmemek için arayanıntargetSdkVersiondeğerini kontrol edebilirsiniz. İstisnalar için, istisnalarınızın uyguladığı ortak bir üst sınıfınız olsun. Böylece bu türü işleyen tüm kodlar, alt türleri de yakalayıp işleyebilir. - Bir geliştiricinin hatayı yanlışlıkla göz ardı etmesi zor veya imkansız olmalıdır. Hatayı bir değer döndürerek bildiriyorsanız yönteminize
@CheckResultekleyin.
Geliştiricinin yanlış yaptığı bir şey nedeniyle (ör. giriş parametrelerindeki kısıtlamaları yoksayma veya gözlemlenebilir durumu kontrol etmeme) bir hata veya başarısızlık koşuluna ulaşıldığında ? extends RuntimeException oluşturmayı tercih edin.
Ayarlayıcı veya işlem (ör. perform) yöntemleri, işlem eşzamansız olarak güncellenen durum veya geliştiricinin kontrolü dışındaki koşullar nedeniyle başarısız olabilirse bir tam sayı durum kodu döndürebilir.
Durum kodları, kapsayan sınıfta public static final alanları olarak tanımlanmalı, ERROR_ ile öneklenmeli ve @hide @IntDef ek açıklamasında numaralandırılmalıdır.
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 diziler yerine Collection türlerini tercih edin
Genel olarak türü belirlenmiş koleksiyon arayüzleri, dizilere kıyasla benzersizlik ve sıralama konusunda daha güçlü API sözleşmeleri, genel türler için destek ve geliştiricilere kolaylık sağlayan çeşitli yöntemler gibi birçok avantaj sunar.
Temel öğelerle ilgili istisna
Öğeler ilkel türlerse otomatik kutulama maliyetini önlemek için dizileri tercih edin. Kutuya alınmış sürümler yerine ham temel öğeleri alma ve döndürme başlıklı makaleyi inceleyin.
Performans açısından hassas kodlarla ilgili istisna
API'nin performansa duyarlı kodda (ör. grafikler veya diğer ölçü/düzen/çizim API'leri) kullanıldığı belirli senaryolarda, ayırmaları ve bellek karmaşıklığını azaltmak için koleksiyonlar yerine diziler kullanmak kabul edilebilir.
Kotlin için istisna
Kotlin dizileri değişmezdir ve Kotlin dili, dizilerle ilgili yeterli yardımcı program API'leri 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 etme
Koleksiyon nesneleri için her zaman @NonNull 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şmez bir koleksiyon nesnesi döndürün.
Tür ek açıklamalarının desteklendiği yerlerde, koleksiyon öğeleri için her zaman @NonNull tercih edin.
Dizileri kullanırken koleksiyonlar yerine @NonNull simgesini de tercih etmeniz gerekir (bkz. önceki öğe). Nesne ayırma işlemi sorun teşkil ediyorsa sabit oluşturup bunu 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;
}
Koleksiyonun değiştirilebilirliği
Kotlin API'leri, API sözleşmesi özellikle değiştirilebilir bir dönüş türü gerektirmediği sürece koleksiyonlar için varsayılan olarak salt okunur (Mutable değil) dönüş türlerini tercih etmelidir.
Ancak Java API'lerinin Android platformundaki uygulaması henüz değişmez koleksiyonların uygun bir uygulamasını sağlamadığından Java API'leri varsayılan olarak değiştirilebilir dönüş türlerini tercih etmelidir. Bu kuralın tek istisnası, değiştirilemeyen Collections.emptydönüş 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 kullanılabileceği durumlarda Java API'leri, koleksiyonun yüzeysel bir kopyasını döndürmeyi kesinlikle düşünmelidir.
@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ştirilebilir dönüş türleri
Koleksiyon döndüren API'ler, döndürdükten sonra döndürülen koleksiyon nesnesini değiştirmemelidir. Döndürülen koleksiyonun değiştirilmesi veya bir şekilde yeniden kullanılması gerekiyorsa (ör. değiştirilebilir bir veri kümesinin uyarlanmış görünümü), içeriklerin ne zaman değiştirilebileceğine dair kesin davranış açıkça belgelenmeli veya yerleşik API adlandırma kurallarına uygun olmalı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şkenliği
Koleksiyon döndüren API'lere benzer şekilde, veri türü nesneleri döndüren API'ler de döndürdükten sonra 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önetirken çok dikkatli olun.
Vararg parametre türünün kullanımı
Geliştiricinin, aynı türden birden fazla ilgili parametreyi iletmek amacıyla çağrı sitesinde bir dizi oluşturmasının muhtemel 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);
Savunma kopyaları
vararg parametrelerinin hem Java hem de Kotlin uygulamaları aynı dizi destekli bayt koduna derlenir ve sonuç olarak Java kodundan değiştirilebilir bir diziyle çağrılabilir. API tasarımcılarının, dizi parametresinin bir alanda veya anonim iç sınıfta kalıcı hale getirileceği durumlarda, dizinin savunma amaçlı bir sığ kopyasını oluşturması şiddetle tavsiye edilir.
public void setValues(SomeObject... values) {
this.values = Arrays.copyOf(values, values.length);
}
Savunma amaçlı bir kopya oluşturmanın, ilk yöntem çağrısı ile kopyanın oluşturulması arasındaki eşzamanlı değişikliklere karşı herhangi bir koruma sağlamadığını ve dizide bulunan nesnelerin mutasyona uğramasına karşı koruma sağlamadığını unutmayın.
Koleksiyon türü parametreleri veya döndürülen türlerle doğru anlamsal yapı sağlamak
List<Foo> varsayılan seçenektir ancak ek anlam sağlamak için diğer türleri de göz önünde bulundurun:
API'niz öğelerin sırasına duyarsızsa ve yinelenen öğelere izin vermiyorsa veya yinelenen öğeler anlamsızsa
Set<Foo>kullanın.API'niz sıraya duyarsızsa ve yinelenen öğelere izin veriyorsa
Collection<Foo>,.
Kotlin dönüşüm işlevleri
Kotlin, .toFoo() ve .asFoo() operatörlerini kullanarak mevcut bir nesneden farklı türde bir nesne elde etmek için sıklıkla kullanır. Burada Foo, dönüştürmenin dönüş türünün adıdır. Bu, bilinen JDK
Object.toString() ile tutarlıdır. Kotlin, 25.toFloat() gibi temel dönüşümler için kullanarak bu özelliği daha da ileriye taşıyor.
.toFoo() ve .asFoo() adlı dönüşümler arasındaki fark önemlidir:
Yeni ve bağımsız bir nesne oluştururken .toFoo() kullanın.
.toString() gibi, "to" dönüşümü 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 yayın oluştururken .asFoo() kullanma
Kotlin'de tür dönüştürme işlemi as anahtar kelimesi kullanılarak gerçekleştirilir. Bu, arayüzdeki bir değişikliği yansıtır ancak kimlikteki bir değişikliği yansıtmaz. Bir uzantı işlevinde önek olarak kullanıldığında .asFoo(), alıcıyı süsler. Orijinal alıcı nesnesindeki bir mutasyon, asFoo() tarafından döndürülen nesneye yansıtılır.
Yeni Foo nesnesindeki 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şlevleri 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 orijinal nesneye yalnızca genel API erişimi gerekir. Bu örnek, bir geliştiricinin kendi tercih ettiği 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 oluşturmamalıdır. Bunun yerine, geliştiricilerin istisnaları aşırı 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 ilgili olmayan hatalar, java.lang.IllegalArgumentException veya java.lang.NullPointerException yerine java.lang.IllegalStateException oluşturmalıdır.
Dinleyiciler ve geri çağırmalar
Bunlar, dinleyici ve geri çağırma mekanizmalarında 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 olayının gerçekleştiğini ve geri çağırmanın buna yanıt vermesi gerektiğini gösterir.
Geçmiş ve şimdiki zaman, zamanlama davranışını açıklamalıdır.
Etkinliklerle ilgili geri çağırma 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, yöntem bir tıklama işlemi gerçekleştirildikten sonra çağrılıyorsa:
public void onClicked()
Ancak, tıklama işlemini gerçekleştirmekten yöntem sorumluysa:
public boolean onClick()
Geri arama kaydı
Bir nesneye işleyici veya geri çağırma eklenebildiğinde ya da nesneden işleyici veya geri çağırma kaldırıldığında, ilişkili yöntemler add ve remove veya register ve unregister olarak adlandırılmalıdır. Sınıfın veya aynı paketteki diğer sınıfların kullandığı mevcut kurala uygun olun. Böyle bir emsal olmadığında ekleme ve kaldırma işlemlerini tercih edin.
Geri çağırmaları kaydetme veya kaydını silme işlemlerini 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ıları kullanmaktan kaçının
getFooCallback() yöntemleri eklemeyin. Bu, geliştiricilerin mevcut bir geri çağırmayı kendi değiştirmeleriyle birlikte zincirlemek isteyebileceği durumlar için cazip bir kaçış yolu olsa da kırılgan bir yöntemdir ve bileşen geliştiricilerin mevcut durum hakkında akıl yürütmesini zorlaştırır. Örneğin,
- A geliştiricisi
setFooCallback(a)numaralı telefonu arar. - B geliştiricisi,
setFooCallback(new B(getFooCallback()))'ı arar. - A geliştiricisi,
ageri çağırmasını kaldırmak istiyor ancakBtürü hakkında bilgi sahibi olmadan bunu yapamıyor.B, sarmalanmış geri çağırmasında bu tür değişikliklere izin verecek şekilde oluşturulmuş.
Geri arama gönderme işlemini kontrol etmek için Executor'ı kabul edin.
Açıkça iş parçacığı oluşturma beklentisi olmayan geri çağırmaları (kullanıcı arayüzü araç setinin dışındaki hemen hemen her yer) kaydederken, geliştiricinin geri çağırmaların hangi iş parçacığında çağrılacağını belirtmesine olanak tanımak için kayıt sırasında Executor parametresinin eklenmesi önemle tavsiye edilir.
public void registerFooCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull FooCallback callback)
@CallbackExecutor ek açıklaması, geliştiricilere yaygın varsayılan seçenekler hakkında bilgi veren otomatik dokümanlar ekleyecek. Ayrıca, Kotlin'de deyimsel kullanımın etkinleştirilmesi için geri çağırma bağımsız değişkeninin en son olması gerektiğini de unutmayın.
İsteğe bağlı parametrelerle ilgili normal Executor� Executor sağlanmazsa geri çağırma, Looper.getMainLooper() kullanılarak ana iş parçacığında çağrılmalı ve bu, ilişkili aşırı yüklenmiş yöntemde belgelenmelidir.
/**
* ...
* 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 uygulama tuzakları: 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 biçimi alan API'leri uygularken uygulama süreci tarafındaki gelen bağlayıcı nesne uygulamanızın, uygulama tarafından sağlanan Executor üzerinde uygulamanın geri çağırmasını çağırmadan önce Binder.clearCallingIdentity() çağırması gerektiği anlamına gelir. Bu sayede, izin kontrolleri için bağlayıcı kimliği (ör. Binder.getCallingUid()) kullanan tüm uygulama kodları, çalışan kodu uygulamaya doğru şekilde atar ve uygulamayı çağıran sistem sürecine atamaz. API'nizin kullanıcıları, arayanın UID veya PID bilgilerini istiyorsa bu bilgiler, Executor sağladıkları yerin çalışmasına bağlı olarak dolaylı değil, API yüzeyinizin açık bir parçası olmalıdır.
Executor belirtme, API'niz tarafından desteklenmelidir. Performansın kritik olduğu durumlarda uygulamaların kodu hemen veya API'nizden gelen geri bildirimlerle senkron olarak çalıştırması gerekebilir. Executor kabul edildiğinde bu işlem yapılabilir.
Tramboline benzer bir HandlerThread öğesini savunma amaçlı olarak oluşturmak bu istenen kullanım alanını engeller.
Bir uygulama kendi sürecinde bir yerde maliyetli 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 yapısı yalnızca tek bir geri çağırma örneğinin desteklenmesini gerektirdiğinde aşağıdaki stili kullanın:
public void setFooCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull FooCallback callback)
public void clearFooCallback()
Handler yerine Executor kullanma
Android'in Handler özelliği, geçmişte geri çağırma yürütmesini belirli bir Looper iş parçacığına yönlendirmek için standart olarak kullanılıyordu. Bu standart, çoğu uygulama geliştiricinin kendi iş parçacığı havuzlarını yönetmesi nedeniyle Executor'ı tercih edecek şekilde değiştirildi. Bu durumda, ana veya kullanıcı arayüzü iş parçacığı, uygulama için kullanılabilen tek Looper iş parçacığı olur. 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 gönderme işlemlerini gerçekleştiren kendi planlama mekanizmalarını sağlar. Bu nedenle, çift iş parçacığı atlamalarından kaynaklanan gecikmeyi önlemek için doğrudan yürütücü (ör. Runnable::run) kullanma olanağı sağlamak önemlidir. Örneğin, Handler kullanılarak Looper iş parçacığına yayınlamak için bir atlama ve ardından uygulamanın eşzamanlılık çerçevesinden başka bir atlama.
Bu yönergeyle ilgili istisnalar nadirdir. İstisna için yapılan yaygın itirazlar şunlardır:
Etkinlikte epoll için Looper gerektiğinden Looper kullanmam gerekiyor.
Executor avantajlarından bu durumda yararlanılamadığından bu istisna isteği kabul edildi.
Uygulama kodunun, etkinliği yayınlayan iş parçacığımı engellemesini istemiyorum. Bu istisna isteği genellikle bir uygulama sürecinde çalışan kod için kabul edilmez. Bu konuda yanlış bilgi veren uygulamalar yalnızca kendilerine zarar verir ve sistemin genel sağlığını etkilemez. Bu konuda doğru yaklaşımı benimseyen veya ortak bir eşzamanlılık çerçevesi kullanan uygulamalar ek gecikme cezası ödememelidir.
Handler, aynı sınıftaki diğer benzer API'lerle yerel olarak tutarlıdır.
Bu istisna talebi duruma bağlı olarak kabul edilir. Executor tabanlı aşırı yüklemelerin eklenmesi ve Handler uygulamalarının yeni Executor uygulamasını kullanacak şekilde taşınması tercih edilir. (myHandler::post geçerli bir Executor!) Sınıfın boyutuna, mevcut Handler yöntemlerinin sayısına ve geliştiricilerin yeni yöntemle birlikte mevcut Handler tabanlı yöntemleri kullanma olasılığına bağlı olarak yeni bir Handler tabanlı yöntem eklemek için istisna verilebilir.
Tescilde simetri
Bir şeyi ekleme veya kaydetme yolu varsa kaldırma/kaydı silme yolu da olmalıdır. Yöntem
registerThing(Thing)
eşleşen bir
unregisterThing(Thing)
İstek tanımlayıcısı sağlama
Geliştiricinin geri aramayı yeniden kullanması makul bir durumsa geri aramayı isteğe bağlamak için bir tanımlayıcı nesne sağlayın.
class RequestParameters {
public int getId() { ... }
}
class RequestExecutor {
public void executeRequest(
RequestParameters parameters,
Consumer<RequestParameters> onRequestCompletedListener) { ... }
}
Birden çok yöntemli geri çağırma nesneleri
Birden fazla yöntemi olan geri çağırmalar interface yöntemini tercih etmeli ve daha önce yayınlanmış arayüzlere ekleme yaparken default yöntemlerini kullanmalıdır. Daha önce bu yönerge, Java 7'de default yöntemlerinin olmaması 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ı modellendirirken android.os.OutcomeReceiver'ı kullanın.
OutcomeReceiver<R,E> başarılı olduğunda R, aksi takdirde E : Throwable sonuç değerini bildirir. Bu, normal bir yöntem çağrısının yapabileceği işlemlerle aynıdır. Sonuç döndüren veya istisna oluşturan bir engelleme yöntemini engellemeyen bir eş zamansı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 değerini döndürür. requestFoo döndürecek tüm sonuçlar, sağlanan executor üzerinde çağrı yapılarak requestFooAsync'nın callback parametresine OutcomeReceiver.onResult bildirilir.
requestFoo tarafından oluşturulan tüm istisnalar, aynı şekilde OutcomeReceiver.onError yöntemine bildirilir.
Asenkron yöntem sonuçlarını bildirmek için OutcomeReceiver kullanmak, androidx.core:core-ktx'deki Continuation.asOutcomeReceiver uzantısını kullanan asenkron yöntemler için bir 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 engellemeyen eşzamansız yöntemleri çağırmasına olanak tanır. Platform API'leri için bu bire bir uzantılar, standart sürüm uyumluluk kontrolleri ve dikkat edilmesi gereken noktalarla birlikte kullanıldığında Jetpack'teki androidx.core:core-ktx yapay nesnesinin bir parçası olarak sunulabilir. Daha fazla bilgi, iptal ile ilgili dikkat edilmesi gereken noktalar ve örnekler için asOutcomeReceiver dokümanlarına bakın.
Çalışması tamamlandığında sonuç döndüren veya hata veren bir yöntemin anlamsal yapısına uymayan eş zamansız 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, java.util.function.*
(referans dokümanları)
türlerini ekledi. Bu türler, geri çağırma lambda'ları olarak kullanıma uygun olan Consumer<T> gibi genel SAM arayüzleri sunar. Çoğu durumda, yeni SAM arayüzleri oluşturmak tür güvenliği veya amacı iletme açısından çok az değer sağlar. Ayrıca, Android API yüzey alanını gereksiz yere genişletir.
Yeni arayüzler oluşturmak yerine şu genel arayüzleri kullanabilirsiniz:
Runnable:() -> UnitSupplier<R>:() -> RConsumer<T>:(T) -> UnitFunction<T,R>:(T) -> RPredicate<T>:(T) -> Boolean- Referans belgelerinde daha birçok örnek bulabilirsiniz.
SAM parametrelerinin yerleşimi
Yöntem ek parametrelerle aşırı yüklenmiş olsa bile, Kotlin'de deyimsel kullanım sağlamak 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 belgeleri (Javadoc) ile ilgili kurallardır.
Tüm herkese açık API'ler belgelendirilmelidir.
Tüm herkese açık API'ler, geliştiricilerin API'yi nasıl kullanacağını açıklayan yeterli dokümanlara sahip olmalıdır. Geliştiricinin, otomatik tamamlama özelliğini kullanarak veya API referans dokümanlarına göz atarken yöntemi bulduğunu ve bitişik API yüzeyinden (ör. aynı sınıf) minimum düzeyde bağlam bilgisine sahip olduğunu varsayalım.
Yöntemler
Yöntem parametreleri ve dönüş değerleri sırasıyla @param ve @return doküman ek açıklamaları kullanılarak belgelendirilmelidir. Javadoc gövdesini, önüne "Bu yöntem..." ifadesi eklenmiş gibi biçimlendirin.
Parametre almayan, özel bir hususu olmayan ve yöntem adının belirttiği işlemi döndüren yöntemlerde @return öğesini atlayabilir ve aşağıdaki gibi dokümanlar yazabilirsiniz:
/**
* Returns the priority of the thread.
*/
@IntRange(from = 1, to = 10)
public int getPriority() { ... }
Javadoc'daki bağlantıları her zaman kullan
Dokümanlar, ilgili sabitler, yöntemler ve diğer öğeler için diğer dokümanlara bağlantı vermelidir. Yalnızca düz metin kelimeler yerine Javadoc etiketlerini (örneğin, @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;
Düz 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 IntDef gibi bir @ValueType ek açıklamasının kullanılması, izin verilen türleri belirten belgeleri otomatik olarak oluşturur. IntDef hakkında daha fazla bilgi için notlarla ilgili kılavuza bakın.
Javadoc eklerken update-api veya docs hedefini çalıştırma
Bu kural, özellikle @link veya @see etiketleri eklerken önemlidir ve çıkışın beklendiği gibi göründüğünden emin olunmalıdır. Javadoc'teki ERROR çıkışı genellikle kötü bağlantılardan kaynaklanır. Bu kontrolü update-api veya docs Make hedefi gerçekleştirir. Ancak yalnızca Javadoc'u değiştiriyorsanız ve başka bir şekilde 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üman metninden ayırt etmek için {@code...} ile sarmalayın.
Kotlin kaynaklarında doküman yazarken kodu, Markdown'da olduğu gibi ters tırnaklarla sarmalayabilirsiniz.
@param ve @return özetleri tek bir cümle parçası olmalıdır.
Parametre ve dönüş değeri özetleri küçük harfle başlamalı ve yalnızca tek bir cümle parçası içermelidir. Tek bir cümleyi aşan 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ümanlardaki notların açıklanması gerekiyor
@hide ve @removed ek açıklamalarının neden genel API'den gizlendiğini belgeleyin.
@deprecated notuyla işaretlenmiş API öğelerinin nasıl değiştirileceğine dair talimatlar ekleyin.
İstisnaları belgelemek için @throws kullanma
Bir yöntem, örneğin IOException gibi kontrol edilmiş bir istisna hatası veriyorsa istisnayı @throws ile belgeleyin. Java istemcileri tarafından kullanılmak üzere Kotlin kaynaklı API'ler için işlevlere @Throws ekleyin.
Bir yöntem, önlenebilir bir hatayı gösteren işaretlenmemiş bir istisna (ör. IllegalArgumentException veya IllegalStateException) oluşturursa istisnayı, neden oluşturulduğunu açıklayarak belgeleyin. Oluşturulan istisna, neden oluşturulduğunu da belirtmelidir.
Bazı kontrol edilmemiş istisna durumları örtülü 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 NullPointerException veya IllegalArgumentException gibi bir @IntDef ya da benzer bir ek açıklamayla eşleşmediği durumlar:
/**
* ...
* @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 zaman uyumsuz kodu çağırıyorsa geliştiricinin bu tür istisnaları nasıl öğrendiğini ve bunlara nasıl yanıt verdiğini göz önünde bulundurun. Bu işlem genellikle istisnanın bir geri çağırmaya yönlendirilmesini ve bunları alan yöntemde oluşturulan istisnaların belgelenmesini içerir. Asenkron istisnalar, açıklama eklenmiş yöntemden gerçekten yeniden oluşturulmadıkları sürece @throws ile belgelendirilmemelidir.
Dokümanların ilk cümlesini nokta ile bitirin.
Doclava aracı, dokümanları basit bir şekilde ayrıştırır ve nokta (.) ile boşluktan sonraki ilk cümleyi, sınıf dokümanlarının üst kısmındaki kısa açıklamada kullanılan özet dokümanı olarak sonlandırır. Bu durum iki soruna neden olur:
- Kısa bir doküman nokta ile bitmiyorsa ve bu üye, araç tarafından alınan dokümanları devraldıysa özet de bu devralınan dokümanları alır. Örneğin, özetine eklenen boyutun açıklamasını içeren
R.attrdokümanlarındakiactionBarTabStylebölümüne bakın. - Aynı nedenle, Doclava özet dokümanlarını "g." ile bitirdiği için ilk cümlede "ör." ifadesini kullanmayın. Örneğin,
View.javabölümündekiTEXT_ALIGNMENT_CENTERkonusuna bakın. Metalava'nın, noktadan sonra kesme içermeyen bir boşluk ekleyerek bu hatayı otomatik olarak düzelttiğini unutmayın. Ancak bu hatayı en başından yapmamaya çalışın.
Dokümanları HTML olarak oluşturulacak şekilde biçimlendirme
Javadoc, HTML olarak oluşturulur. Bu nedenle, bu dokümanları buna göre 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.
Listelerde, sıralanmamış ve sıralanmış öğeler için sırasıyla
<ul>veya<ol>kullanılmalıdır. Her öğe bir<li>etiketiyle başlamalıdır ancak kapanış</li>etiketi gerekmez. Son öğeden sonra kapanış</ul>veya</ol>etiketi gerekir.Tablolarda satırlar için
<table>,<tr>, başlıklar için<th>ve hücreler için<td>kullanılmalıdır. Tüm tablo etiketleri, eşleşen kapatma etiketleri gerektirir. Kullanımdan kaldırılmayı belirtmek için herhangi bir etiketteclass="deprecated"simgesini kullanabilirsiniz.Satır içi kod yazı tipi oluşturmak için
{@code foo}simgesini kullanın.Kod blokları oluşturmak için
<pre>kullanın.<pre>bloğunun içindeki tüm metinler tarayıcı tarafından ayrıştırılır. Bu nedenle, parantezleri<>kullanırken dikkatli olun.<ve>HTML öğeleriyle bu karakterlerden kaçabilirsiniz.Alternatif olarak, hata içeren bölümleri
{@code foo}ile sarmalarsanız kod snippet'inizde ham köşeli parantezler<>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 How to Write Doc Comments for the Javadoc Tool (Javadoc Aracı İçin Belge Yorumları Yazma) başlıklı resmi Java dil yönergelerindeki önerilere uyun.
Android Framework'e özgü kurallar
Bu kurallar, Android çerçevesine yerleştirilmiş API'lere ve davranışlara (ör. Bundle veya Parcelable) özgü API'ler, kalıplar ve veri yapılarıyla ilgilidir.
Amaç oluşturucular, create*Intent() kalıbını kullanmalıdır.
Niyetler için içerik üreticiler, createFooIntent() adlı yöntemleri kullanmalıdır.
Yeni genel amaçlı veri yapıları oluşturmak yerine Bundle'ı kullanma
Rastgele anahtar-türlenmiş 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ı olarak işlev 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 platform dışında (ör. Jetpack kitaplığında) tanımlanabilir.
Platformun verileri okuduğu durumlarda Bundle kullanmaktan kaçının ve kesin olarak türü belirlenmiş bir veri sınıfını tercih edin.
Parcelable uygulamalarında herkese açık CREATOR alanı olmalıdır
Parcelable şişirme, ham oluşturucular aracılığıyla değil CREATOR aracılığıyla kullanıma sunulur. Bir sınıf Parcelable uyguluyorsa CREATOR alanı da herkese açık bir API olmalı ve Parcel bağımsız değişkenini alan sınıf oluşturucu özel olmalıdır.
Kullanıcı arayüzü dizeleri için CharSequence kullanma
Bir dize kullanıcı arayüzünde gösterildiğinde CharSequence kullanarak Spannable örneklerine izin verin.
Yalnızca bir anahtar veya kullanıcılar tarafından görünmeyen başka bir etiket ya da değerse String uygundur.
Numaralandırmalar kullanmaktan kaçının
IntDef
tüm platform API'lerinde numaralandırmalar yerine kullanılmalı ve kitaplık API'lerinde de kesinlikle göz önünde bulundurulmalıdır. Numaralandırılmış değerleri yalnızca yeni değerler eklenmeyeceğinden emin olduğunuzda kullanın.
IntDef avantajları:
- Zaman içinde değer eklemeyi sağlar.
- Kotlin
whenifadeleri, platforma eklenen bir enum değeri nedeniyle artık kapsamlı olmaması durumunda çalışma zamanında başarısız olabilir.
- Kotlin
- Çalışma zamanında sınıf veya nesne kullanılmaz, yalnızca temel öğeler kullanılır.
- R8 veya küçültme, paketlenmemiş kitaplık API'leri için bu maliyeti önleyebilir ancak bu optimizasyon, platform API sınıflarını etkileyemez.
Enum'un avantajları
- Java ve Kotlin'in deyimsel dil özelliği
- Kapsamlı switch,
whendeyiminin kullanılmasını sağlar.- Not: Değerler zaman içinde değişmemelidir. Önceki listeye bakın.
- Net kapsamlı ve bulunabilir adlandırma
- Derleme zamanı doğrulamayı etkinleştirir.
- Örneğin, Kotlin'de değer döndüren bir
whenifadesi
- Örneğin, Kotlin'de değer döndüren bir
- Arayüzleri uygulayabilen, statik yardımcıları olan, üye veya uzantı yöntemlerini ve alanları kullanıma sunabilen çalışan bir sınıftır.
Android paket katmanlama hiyerarşisine uyun
android.* paket hiyerarşisinde, alt düzey paketlerin üst düzey paketlere bağlı olamayacağı örtülü bir sıralama vardır.
Google'dan, diğer şirketlerden ve ürünlerinden bahsetmeyin.
Android platformu, açık kaynaklı bir projedir ve tedarikçiden bağımsız olmayı amaçlar. API genel olmalı ve gerekli izinlere sahip sistem entegratörleri veya uygulamalar tarafından eşit şekilde kullanılabilmelidir.
Parcelable uygulamaları nihai olmalıdır
Platform tarafından tanımlanan Parcelable sınıfları her zaman framework.jar konumundan yüklendiğinden bir uygulamanın Parcelable uygulamasını geçersiz kılmaya çalışması geçersizdir.
Gönderen uygulama bir Parcelable uzatırsa alan uygulama, gönderenin özel uygulamasını açamaz. Geriye dönük uyumlulukla ilgili not: Sınıfınız geçmişte nihai olmamasına rağmen herkese açık bir oluşturucuya sahip değilse yine de final olarak işaretleyebilirsiniz.
Sistem sürecini çağıran yöntemler, RemoteException'ı RuntimeException olarak yeniden oluşturmalıdır.
RemoteException genellikle dahili AIDL tarafından oluşturulur ve sistem sürecinin sonlandığını veya uygulamanın çok fazla veri göndermeye çalıştığını gösterir. Her iki durumda da genel API, uygulamaların güvenlik veya politika kararlarını kalıcı hale getirmesini önlemek için RuntimeException olarak yeniden oluşturulmalıdır.
Bir Binder çağrısının diğer tarafının sistem süreci olduğunu biliyorsanız bu ortak metin kodu, en iyi uygulamadır:
try {
...
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
API değişiklikleri için belirli istisnalar oluşturma
Herkese açık API davranışları, API düzeyleri arasında değişebilir ve uygulama çökmelerine neden olabilir (ör. yeni güvenlik politikalarını zorunlu kılmak için).
API'nin daha önce geçerli olan bir istek için hata vermesi gerektiğinde genel bir hata yerine yeni ve spesifik bir hata verin. Örneğin, SecurityException yerine ExportedFlagRequired (ve ExportedFlagRequired, SecurityException'yi uzatabilir).
Bu sayede uygulama geliştiriciler ve araçlar, API davranış değişikliklerini algılayabilir.
Klon yerine kopyalama oluşturucuyu uygulama
clone() sınıfının sağladığı API sözleşmelerinin olmaması ve clone() kullanan sınıfların genişletilmesindeki zorluklar nedeniyle Java clone() yönteminin kullanılması kesinlikle önerilmez.Object Bunun yerine, aynı türde bir nesne alan bir kopya oluşturucu kullanın.
/**
* Constructs a shallow copy of {@code other}.
*/
public Foo(Foo other)
Oluşturma için Oluşturucu'ya dayanan sınıflar, kopyada değişiklik yapılmasına izin vermek için Oluşturucu kopyalama oluşturucusu 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ıf olduğundan, bu durum belirsiz kullanımdan sonra kapatma hatalarına neden olabilir. Bunun yerine API'ler ParcelFileDescriptor örneklerini döndürmeli veya kabul etmelidir. Eski kod, gerekirse dup() veya getFileDescriptor() kullanılarak PFD ile FD arasında dönüştürme yapabilir.
Tek boyutlu sayısal değerler kullanmaktan kaçının
short veya byte değerlerini doğrudan kullanmaktan kaçının. Bu değerler, API'yi gelecekte geliştirme şeklinizi genellikle sınırlar.
BitSet kullanmaktan kaçının
java.util.BitSet, uygulama için idealdir ancak genel 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 neyi temsil ettiğine dair anlamsal bir anlam sağlamaz.
Yüksek performanslı senaryolar için @IntDef ile birlikte int veya long kullanın. Düşük performanslı senaryolar için Set<EnumType> kullanmayı düşünebilirsiniz. İşlenmemiş ikili veriler için byte[] kullanın.
android.net.Uri 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 kullanmaktan 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şaretlenen ek açıklamaları gizleme
@IntDef, @LongDef veya @StringDef olarak işaretlenen açıklamalar, bir API'ye iletilebilecek geçerli sabitler kümesini ifade eder. Ancak, API olarak dışa aktarıldıklarında derleyici, sabitleri satır içi yapar ve yalnızca (artık işe yaramayan) değerler, ek açıklamanın API kaba kodunda (platform için) veya JAR'da (kitaplıklar için) kalır.
Bu nedenle, bu ek açıklamaların kullanımları platformda @hide docs
ek açıklamasıyla veya kitaplıklarda @RestrictTo.Scope.LIBRARY) code ek açıklamasıyla işaretlenmelidir. API saplarında veya JAR'larda görünmelerini önlemek için her iki durumda da @Retention(RetentionPolicy.SOURCE) olarak işaretlenmeleri gerekir.
@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'leri oluşturulurken bir araç, ek açıklamaları ayıklar ve derlenmiş kaynaklardan ayrı olarak paketler. Android Studio, bu paketlenmiş biçimi okur ve tür tanımlarını zorunlu kılar.
Yeni ayar sağlayıcı anahtarları eklemeyin
Settings.Global, Settings.System veya Settings.Secure kaynaklı yeni anahtarları kullanıma sunmayın.
Bunun yerine, ilgili sınıfa (genellikle bir "yönetici" sınıfı) uygun bir getter ve setter Java API'si ekleyin. Gerekli olduğunda istemcileri değişikliklerden haberdar etmek için bir dinleyici mekanizması veya yayın ekleyin.
SettingsProvider ayarlarının, get/set yöntemlerine kıyasla çeşitli sorunları vardır:
- Tip güvenliği yoktur.
- Varsayılan değer sağlamak için birleştirilmiş bir yöntem yoktur.
- İzinleri özelleştirmenin uygun bir yolu yoktur.
- Örneğin, ayarlarınızı özel bir izinle koruyamazsınız.
- Özel mantığı düzgün bir şekilde eklemenin uygun bir yolu yoktur.
- Örneğin, B ayarının değerine bağlı olarak A ayarının değerini değiştirmek mümkün değildir.
Örnek:
Settings.Secure.LOCATION_MODE
uzun süredir kullanılıyordu ancak konum ekibi, LocationManager.isLocationEnabled()
adlı uygun bir Java API'si ve MODE_CHANGED_ACTION
yayınını kullanıma sundu. Bu sayede ekip çok daha fazla esneklik kazandı ve API'lerin anlamları artık çok daha net.
Activity ve AsyncTask'i genişletmeyin
AsyncTask, bir uygulama ayrıntısıdır. Bunun yerine bir dinleyiciyi veya androidx'te bir ListenableFuture API'yi kullanın.
Activity alt sınıfları oluşturmak mümkün değildir. Özelliğinizin etkinliğini genişletmek, bu özelliği kullanıcıların aynı işlemi yapmasını gerektiren diğer özelliklerle uyumsuz hale getirir. Bunun yerine, LifecycleObserver gibi araçları kullanarak kompozisyondan yararlanın.
Bağlamın getUser() işlevini kullanma
Context ile ilişkili sınıflar (ör. Context.getSystemService() tarafından döndürülen her şey), belirli kullanıcıları hedefleyen üyeleri göstermek yerine Context ile ilişkili 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: Tek bir kullanıcıyı temsil etmeyen değerleri (ör. UserHandle.ALL) kabul eden bir yöntem, kullanıcı bağımsız değişkenini kabul edebilir.
Düz tamsayılar yerine UserHandle kullanın
UserHandle, tür güvenliği sağlamak ve kullanıcı kimliklerinin uid'lerle karıştırılmasını önlemek için tercih edilir.
Foobar getFoobarForUser(UserHandle user);
Foobar getFoobarForUser(int userId);
Kaçınılmaz durumlarda, kullanıcı kimliğini temsil eden bir int, @UserIdInt ile açıklama eklenmelidir.
Foobar getFoobarForUser(@UserIdInt int user);
Yayın amaçları yerine dinleyicileri veya geri aramaları tercih etme
Yayın amaçları çok güçlüdür ancak sistem sağlığını olumsuz etkileyebilecek beklenmedik davranışlara yol açtıkları için yeni yayın amaçları dikkatli bir şekilde eklenmelidir.
Yeni yayın amaçlarının kullanıma sunulmasını önermememizin nedenleri arasında şunlar yer alır:
FLAG_RECEIVER_REGISTERED_ONLYişareti olmadan yayın gönderirken halihazırda çalışmayan uygulamaları zorla başlatır. Bu bazen amaçlanan bir sonuç olsa da düzinelerce uygulamanın aynı anda çalışmasına neden olarak sistemin sağlığını olumsuz etkileyebilir. Çeşitli ön koşullar karşılandığında daha iyi koordinasyon sağlamak içinJobSchedulergibi alternatif stratejiler kullanmanızı öneririz.Yayın gönderirken uygulamalara gönderilen içerikleri filtreleme veya ayarlama imkanı sınırlıdı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 yapmayı zorlaştırır ya da imkansız hâle getirir.
Yayın kuyrukları paylaşılan bir kaynak olduğundan aşırı yüklenebilir ve etkinliğinizin zamanında yayınlanmamasına neden olabilir. Gerçek hayatta, uçtan uca gecikme süresi 10 dakika veya daha uzun olan birkaç yayın sırası tespit ettik.
Bu nedenlerden dolayı, yeni özelliklerin yayın amaçları yerine dinleyicileri, geri çağırmaları veya JobScheduler gibi diğer olanakları kullanmayı düşünmesini öneririz.
Yayın amaçlarının hâlâ ideal tasarım olduğu durumlarda göz önünde bulundurulması gereken bazı en iyi uygulamalar şunlardır:
- Mümkünse
Intent.FLAG_RECEIVER_REGISTERED_ONLYkullanarak yayınınızı halihazırda çalışan uygulamalarla sınırlayın. Örneğin,ACTION_SCREEN_ONuygulamaların uyandırılmasını önlemek için bu tasarımı kullanır. - Mümkünse yayını belirli bir ilgi alanı uygulamasına hedeflemek için
Intent.setPackage()veyaIntent.setComponent()kullanın. Örneğin,ACTION_MEDIA_BUTTON, oynatma kontrollerini işleyen mevcut uygulamaya odaklanmak için bu tasarımı kullanır. - Mümkünse yayınınızı
<protected-broadcast>olarak tanımlayarak kötü amaçlı uygulamaların işletim sisteminin kimliğine bürünmesini önleyin.
Sisteme bağlı geliştirici hizmetlerindeki amaçlar
Geliştirici tarafından genişletilmesi ve sistem tarafından sınırlandırılması 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:
- Hizmetin tam nitelikli sınıf adını içeren sınıfta bir
SERVICE_INTERFACEdize sabiti tanımlayın. Bu sabit,@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)ile açıklama eklenmelidir. - Geliştiricinin platformdan niyet almak için
<intent-filter>'ıAndroidManifest.xml'ına eklemesi gerektiğini belirten doküman. - Kötü amaçlı uygulamaların geliştirici hizmetlerine
Intentgöndermesini önlemek için sistem düzeyinde izin eklemeyi önemle tavsiye ederiz.
Kotlin-Java birlikte çalışabilirlik
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 yönergeler bu kılavuza kopyalandı.
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 sunulma şeklini etkileyerek hata ayıklamayı zorlaştırır.
Belirli yönergeler 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 bunlar, kapsayan sınıfta değil, Companion adlı bir iç sınıfta Java'dan gösterilir. Companion sınıfları, API metin dosyalarında boş sınıflar olarak gösterilebilir. Bu durum, amaçlandığı şekilde çalışmaktadır.
Java ile uyumluluğu en üst düzeye çıkarmak için, yardımcı nesnelerin sabit olmayan alanlarını @JvmField ile, public işlevlerini ise @JvmStatic ile açıklama ekleyerek bunları doğrudan içeren sınıfta kullanıma sunun.
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 uyumluluğu bozan değişiklikler
Sonlandırılmış genel API yüzeylerinde ikili uyumluluğu bozan değişikliklerden kaçının. Bu tür değişiklikler genellikle make update-api çalıştırılırken hatalara neden olur ancak Metalava'nın API kontrolünün yakalamadığı uç durumlar olabilir. Şüpheye düştüğünüzde, Java'da hangi API değişikliklerinin uyumlu olduğuyla ilgili ayrıntılı açıklama için Eclipse Foundation'ın Evolving Java-based APIs (Java Tabanlı API'leri Geliştirme) kılavuzuna bakın. Gizli (ör. sistem) API'lerdeki ikili uyumluluğu bozan değişiklikler, kullanımdan kaldırma/değiştirme döngüsüne uygun olmalıdır.
Kaynakta yapılan, uyumluluğu bozan değişiklikler
İkili uyumluluğu bozmayan değişiklikler olsa bile kaynak uyumluluğunu bozan değişiklikleri önermiyoruz. İkili uyumlu ancak kaynakta değişiklik gerektiren bir değişikliğe örnek olarak, mevcut bir sınıfa genel bir öğe eklemek verilebilir. Bu değişiklik ikili uyumludur ancak devralma veya belirsiz referanslar nedeniyle derleme hatalarına yol açabilir.
Kaynakta değişiklik yapılmasına neden olan değişiklikler make update-api çalıştırılırken hataya yol açmaz. Bu nedenle, mevcut API imzalarında yapılan değişikliklerin etkisini anlamaya özen göstermeniz gerekir.
Bazı durumlarda, geliştirici deneyimini veya kod doğruluğunu iyileştirmek için kaynakta uyumluluğu bozan değişiklikler yapılması gerekir. Örneğin, Java kaynaklarına nullability ek açıklamaları eklemek Kotlin koduyla birlikte çalışabilirliği artırır ve hata olasılığını azaltır ancak genellikle kaynak kodda değişiklikler (bazen önemli değişiklikler) yapılmasını gerektirir.
Özel API'lerde yapılan değişiklikler
@TestApi ile açıklama eklenen API'leri istediğiniz zaman değiştirebilirsiniz.
@SystemApi ile açıklama eklenen API'leri üç yıl boyunca korumanız gerekir. Aşağıdaki programa göre bir sistem API'sini kaldırmanız veya yeniden düzenlemeniz gerekir:
- API y - Added
- API y+1 - Kullanımdan Kaldırma
- Kodu
@Deprecatedile işaretleyin. - Yerine kullanılacak kodları ekleyin ve
@deprecateddocs ek açıklamasını kullanarak, Javadoc'ta kullanımdan kaldırılan kodun yerine kullanılacak kodlara bağlantı verin. - Geliştirme döngüsü sırasında, API'nin desteğinin sonlandırılacağını belirterek kurum içi kullanıcılara karşı hata kaydı oluşturun. Bu, değiştirilen API'lerin yeterli olduğunu doğrulamaya yardımcı olur.
- Kodu
- API y+2 - Geçici kaldırma
- Kodu
@removedile işaretleyin. - İsteğe bağlı olarak, yayın için mevcut SDK düzeyini hedefleyen uygulamalarda hata vermek veya hiçbir işlem yapmamak.
- Kodu
- API y+3 - Kesin kaldırma
- Kodu kaynak ağacından tamamen kaldırın.
Kullanımdan kaldırma
Desteğin sonlandırılması bir API değişikliği olarak kabul edilir ve ana sürümde (ör. harf) gerçekleşebilir. API'lerin desteği sonlandırılırken @Deprecated kaynak notu ve @deprecated
<summary> doküman notunu birlikte kullanın. Özetinizde taşıma stratejisi olmalıdır. Bu strateji, yerine geçecek 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 kullanıma sunulan özellikler ve stil verilebilen özellikler de dahil olmak üzere XML'de tanımlanan ve Java'da kullanıma sunulan API'lerin desteğini de sonlandırmanız gerekir. Bu API'lerin desteğini sonlandırma işlemini özetle açıklayın:
<!-- Attribute whether the accessibility service ...
{@deprecated Not used by the framework}
-->
<attr name="canRequestEnhancedWebAccessibility" format="boolean" />
API'nin desteği ne zaman sonlandırılır?
Desteğin sonlandırılması, bir API'nin yeni kodda kullanılmasını engellemek için en yararlı yöntemdir.
Bir API'nin desteğini sonlandırmadan önce geliştiriciler üzerindeki etkisini göz önünde bulundurun. API desteğinin sonlandırılmasının etkileri şunlardır:
javac, derleme sırasında uyarı verir.- Kullanımdan kaldırma uyarıları genel olarak devre dışı bırakılamaz veya temel alınamaz. Bu nedenle,
-Werrorkullanan geliştiricilerin derleme SDK'sı sürümlerini güncelleyebilmeleri için, kullanımdan kaldırılan API'lerin her kullanımını tek tek düzeltmesi veya devre dışı bırakması gerekir. - Kullanımdan kaldırılan sınıfların içe aktarılmasıyla ilgili kullanım dışı bırakma uyarıları gizlenemez. Bu nedenle, geliştiricilerin derleme SDK'sı sürümlerini güncelleyebilmeleri için, kullanımdan kaldı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.
- Kullanımdan kaldırma uyarıları genel olarak devre dışı bırakılamaz veya temel alınamaz. Bu nedenle,
d.android.comile ilgili dokümanlarda desteğin sonlandırılacağı bildirimi gösteriliyor.- Android Studio gibi IDE'ler, API kullanım sitesinde uyarı gösterir.
- IDE'ler, API'nin otomatik tamamlama özelliğinde sıralamasını düşürebilir veya API'yi 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ı kullanmasını engelleyebilir.
Mevcut kodlarındaki uyarıları önemsemeyen geliştiriciler, desteği sonlandırılan özellikleri tamamen göz ardı edebilir.
Çok sayıda desteği sonlandırılan işlev içeren bir SDK, bu iki durumu da kötüleştirir.
Bu nedenle, API'lerin yalnızca aşağıdaki durumlarda desteğinin sonlandırılmasını öneririz:
- API'yi gelecekteki bir sürümde
@removeplanlıyoruz. - API kullanımı, uyumluluğu bozmadan düzeltemeyeceğ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 karşılık gelen bir uyumluluk API'si eklemenizi şiddetle tavsiye ederiz.
Mevcut ve gelecekteki sürümlerde beklendiği gibi çalışan API'lerin desteğinin sonlandırılmasını önermiyoruz:
/**
* ...
* @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;
Kullanımdan kaldırılan API'lerde yapılan değişiklikler
Kullanımdan kaldırılan API'lerin davranışını korumanız gerekir. Bu nedenle, test uygulamaları aynı kalmalı ve API'nin desteğini sonlandırdıktan sonra testler geçmeye devam etmelidir. API'de test yoksa test eklemeniz gerekir.
Gelecekteki sürümlerde, desteği sonlandırılan API yüzeylerini genişletmeyin. Mevcut bir kullanımdan kaldırılmış API'ye lint doğruluk notları (örneğin, @Nullable) ekleyebilirsiniz ancak yeni API'ler eklememelisiniz.
Yeni API'leri desteği sonlandırılmış olarak eklemeyin. Bir ön sürüm döngüsünde eklenip daha sonra desteği sonlandırılan API'ler varsa (bu nedenle başlangıçta herkese açık API yüzeyine desteği sonlandırılmış olarak girerler) API'yi tamamlamadan önce bunları kaldırmanız gerekir.
Kısmi olarak kaldırma
Desteğin sonlandırılması, kaynakta değişiklik yapılmasına neden olan bir işlemdir ve API Konseyi açıkça onaylamadığı sürece herkese açık API'lerde bu işlemden kaçınmanız gerekir.
Sistem API'leri için, API'yi geçici olarak kaldırmadan önce ana sürüm süresince desteğini sonlandırmanız gerekir. API'lerle ilgili tüm doküman referanslarını kaldırın ve API'leri geçici olarak kaldırırken @removed <summary> doküman açıklamasını kullanın. Özetinizde, kaldırma nedeni belirtilmelidir. Ayrıca Desteğin Sonlandırılması başlıklı makalede açıkladığımız gibi bir taşıma stratejisi de yer alabilir.
Geçici olarak kaldırılan API'lerin davranışı olduğu gibi korunabilir ancak daha da önemlisi, mevcut arayanların API'yi çağırırken kilitlenmemesi için korunmalıdır. Bazı durumlarda bu, davranışın korunması anlamına gelebilir.
Test kapsamı korunmalıdır ancak testlerin içeriğinin davranışsal değişikliklere uyum sağlamak için değiştirilmesi gerekebilir. Testler, mevcut arayanların çalışma zamanında kilitlenmediğini doğrulamaya devam etmelidir. Geçici olarak 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 bu davranışı korumanız gerekir. Bazı durumlarda bu, davranışın korunması anlamına gelebilir.
Test kapsamını korumanız gerekir ancak testlerin içeriğinin davranış değişikliklerine uyum sağlayacak şekilde değiştirilmesi gerekebilir. Testler, mevcut arayanların çalışma zamanında kilitlenmediğini doğrulamaya devam etmelidir.
Teknik düzeyde, @remove Javadoc ek açıklamasını kullanarak API'yi SDK kaba kod JAR'ından ve derleme zamanı sınıf yolundan kaldırırız. Ancak API, @hide API'lerine benzer şekilde çalışma zamanı sınıf yolunda varlığını sürdürür:
/**
* Ringer volume. This is ...
*
* @removed Not functional since API 2.
*/
public static final String VOLUME_RING = ...
Uygulama geliştiriciler açısından API artık otomatik tamamlama özelliğinde görünmüyor ve API'ye referans veren kaynak kodu, compileSdk, API'nin kaldırıldığı SDK'ya eşit veya daha yeni olduğunda derlenmiyor. Ancak kaynak kodu, API'ye referans veren önceki SDK'lara ve ikili dosyalara karşı başarılı bir şekilde derlenmeye devam ediyor.
Belirli API kategorileri geçici olarak kaldırılmamalıdır. Belirli API kategorilerini kaldırmamalısınız.
Soyut yöntemler
Geliştiricilerin genişletebileceği sınıflardaki soyut yöntemleri geçici olarak kaldırmamalısınız. Bu durumda geliştiricilerin sınıfı tüm SDK düzeylerine başarılı bir şekilde genişletmesi mümkün olmaz.
Geliştiricilerin bir sınıfı genişletmesinin hiçbir zaman mümkün olmadığı ve olmayacağı nadir durumlarda, soyut yöntemleri yine de geçici olarak kaldırabilirsiniz.
Kalıcı kaldırma
Kesin kaldırma, ikili uyumluluğu bozan bir değişikliktir ve herkese açık API'lerde asla gerçekleşmemelidir.
Önerilmeyen ek açıklama
Çoğu durumda (%95'ten fazla) bir API'yi önermediğimizi belirtmek için @Discouraged açıklamasını kullanırız. Kullanımı önerilmeyen API'ler, dar kapsamlı kritik bir kullanım alanı nedeniyle kullanımdan kaldırılmayan API'lerden farklıdır. Bir API'yi önerilmeyen olarak işaretlediğinizde açıklama ve alternatif çö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);
}
Yeni API'ler eklemeniz önerilmez.
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ştiriciler Binder üzerinden gönderilemeyecek kadar büyük etkinlikler yayınlamaya çalıştığında bunu net bir şekilde bildirmek için DropBoxManager özelliğini iyileştirdik.
Ancak mevcut uygulamalarda sorunlara yol açmamak için eski uygulamalarda güvenli davranışın korunmasını önemle tavsiye ederiz. Geçmişte bu davranış değişikliklerini uygulamanın ApplicationInfo.targetSdkVersion temelinde koruyorduk ancak kısa süre önce Uygulama Uyumluluğu Çerçevesi'nin kullanılmasını zorunlu kıldık. Bu yeni çerçeveyi kullanarak davranış değişikliğini nasıl uygulayacağınızla ilgili bir örneği aşağıda bulabilirsiniz:
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 uyumluluğu çerçevesi tasarımını kullanmak, geliştiricilerin uygulamalarının hatalarını ayıklarken belirli davranış değişikliklerini önizleme ve beta sürümlerinde geçici olarak devre dışı bırakmalarını sağlar. Böylece, aynı anda düzinelerce davranış değişikliğine uyum sağlamak zorunda kalmazlar.
İleriye dönük uyumluluk
İleri uyumluluk, bir sistemin kendisinin daha sonraki bir sürümü için tasarlanmış girişi kabul etmesine olanak tanıyan bir tasarım özelliğidir. API tasarımında, geliştiriciler kodu bir kez yazıp bir kez test etmeyi ve her yerde sorunsuz çalıştırmayı beklediğinden ilk tasarımın yanı sıra gelecekteki değişikliklere de özellikle dikkat etmeniz gerekir.
Android'de en sık karşılaşılan ileri uyumluluk sorunlarına aşağıdakiler neden olur:
- Daha önce tamamlanmış olduğu varsayılan bir kümeye (ör.
@IntDefveyaenum) yeni sabitler ekleme (ör.switch'nin bir istisna oluşturandefault'ye sahip olduğu durumlar). - API yüzeyinde doğrudan yakalanmayan bir özellik için destek ekleme (ör. daha önce yalnızca
<color>kaynakları desteklenirken XML'deColorStateListtürü kaynakları atama desteği). - Çalışma zamanı kontrolleriyle ilgili kısıtlamaların gevşetilmesi (ör. daha eski sürümlerde bulunan bir
requireNotNull()kontrolünün kaldırılması).
Tüm bu durumlarda geliştiriciler, bir sorun olduğunu yalnızca çalışma zamanında öğrenir. Daha da kötüsü, bu sorunları sahada kullanılan eski cihazlardan gelen kilitlenme raporları sayesinde öğrenebilirler.
Ayrıca, bu durumların tümü teknik olarak geçerli API değişiklikleridir. İkili veya kaynak uyumluluğunu bozmazlar 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 düşük sürümlerde başarısız olmasına neden olacak mı?" sorusunu sorun.
XML şemaları
Bir XML şeması bileşenler arasında sabit 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ştirilmelidir. Örneğin, XML öğelerinin ve özelliklerinin yapısı, diğer Android API yüzeylerinde yöntemlerin ve değişkenlerin korunma şekline 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şaretini ekleyebilirsiniz ancak normal @SystemApi geliştirme 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, sequence öğesini, choice öğesini ve all öğelerini complexType öğesinin alt öğeleri olarak destekler. Ancak bu alt öğeler, alt öğelerinin sayısı ve sırası bakımından farklılık gösterdiğinden mevcut bir türü değiştirmek uyumsuz bir değişiklik olur.
Mevcut bir türü değiştirmek istiyorsanız en iyi uygulama, eski türü kullanımdan kaldırmak ve yerine yeni bir türü kullanıma sunmaktı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 hat modülüne özgü kalıplar
Mainline, Android OS'in alt sistemlerinin ("Mainline modülleri") tüm sistem görüntüsünü güncellemek yerine ayrı ayrı güncellenmesine olanak tanıyan bir projedir.
Ana hat modüllerinin temel platformdan "ayrılması" gerekir. Bu, her modül ile dünyanın geri kalanı arasındaki tüm etkileşimlerin resmi (genel veya sistem) API'ler kullanılarak yapılması gerektiği anlamına gelir.
Ana hat modüllerinin uyması gereken belirli tasarım kalıpları vardır. Bu bölümde bu türler açıklanmaktadır.
<Module>FrameworkInitializer kalıbı
Bir ana hat modülünün @SystemService sınıflarını (örneğin, JobScheduler) kullanıma sunması gerekiyorsa aşağıdaki kalıbı kullanın:
Modülünüzden bir
<YourModule>FrameworkInitializersınıfını kullanıma sunun. Bu sınıfın$BOOTCLASSPATHiçinde olması gerekir. Örnek: StatsFrameworkInitializer@SystemApi(client = MODULE_LIBRARIES)ile işaretleyin.public static void registerServiceWrappers()yöntemi ekleyin.Bir hizmet yöneticisi sınıfı,
Contextöğesine referans gerektirdiğinde bu sınıfı kaydetmek içinSystemServiceRegistry.registerContextAwareService()öğesini kullanın.Bir hizmet yöneticisi sınıfını
SystemServiceRegistry.registerStaticService()kullanarakContextöğesine referans gerekmediğinde kaydedin.SystemServiceRegistry'ı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ğundan bunu kullanamaz. Bu sınıf, ana hat modüllerinin statik platform veya diğer modüller tarafından kullanıma sunulan sisteme ait hizmet bağlayıcı nesnelerini kaydetmemesi ya da bunlara başvurmaması gerektiğinden gizlenir.
Ana hat modülleri, modülün içinde uygulanan bağlayıcı hizmetlere kaydolabilmek ve bunlara referans alabilmek için bunun yerine aşağıdaki kalıbı kullanabilir.
TelephonyServiceManager tasarımını takip ederek
<YourModule>ServiceManagersınıfı oluşturun.Sınıfı
@SystemApiolarak kullanıma sunun. Yalnızca$BOOTCLASSPATHsı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 şunlardan oluşur:
- Gizli bir oluşturucu olduğundan yalnızca statik platform kodu bunu örnekleyebilir.
- Belirli bir ad için
ServiceRegistererörneği döndüren herkese açık getter yöntemleri. Bir bağlayıcı nesneniz varsa bir alıcı yönteme ihtiyacınız vardır. İki değişkeniniz varsa iki alıcıya ihtiyacınız vardır. ActivityThread.initializeMainlineModules()içinde bu sınıfı oluşturun ve modülünüz tarafından kullanıma sunulan statik bir yönteme iletin. Normalde,@SystemApi(client = MODULE_LIBRARIES)API'sini alanFrameworkInitializersınıfınıza statik olarak 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ülebilse de diğer modüllerin <YourModule>ServiceManager örneği almasının mümkün olmamasıdır.
Telefon hizmetinin, telefon hizmetine nasıl referans verdiği kod arama bağlantısında açıklanmaktadır.
Yerel kodda bir hizmet bağlayıcı nesnesi uyguluyorsanız AServiceManager yerel API'lerini kullanırsınız.
Bu API'ler, ServiceManager Java API'lerine karşılık gelir ancak yerel olanlar doğrudan ana hat modüllerine sunulur. Bunları, modülünüze ait olmayan bağlayıcı nesneleri kaydetmek veya bunlara başvurmak için kullanmayın. Yerel koddan bir bağlayıcı nesne kullanıma sunuyorsanız <YourModule>ServiceManager.ServiceRegisterer öğenizin register() yöntemi olması gerekmez.
Ana hat modüllerindeki izin tanımları
APK'lar içeren Mainline modülleri, APK'larında (özel) izinleri normal bir APK ile aynı şekilde AndroidManifest.xml 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 iznin, güncellenebilir bir platform API'si kapsamında diğer uygulamalara sağlanması gerekiyorsa izin adının başına "android.permission." eklenmelidir. (Herhangi bir statik platform izni gibi) artı modül paket adı, adlandırma çakışmalarını önlerken modüldeki bir 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 sabiti olarak kullanıma sunabilir. Örneğin, HealthPermissions.READ_ACTIVE_CALORIES_BURNED.