Bu sayfa, API Konseyi'nin API incelemelerinde uyguladığı genel ilkeleri anlamaları için geliştiricilere yönelik bir kılavuz 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ç, kuralların çoğunu API'lere karşı çalıştırdığı kontrollerde kodlar.
Bu dokümanı, 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üşünebilirsiniz.
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. Bu işlemi, 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 bağlı kalmak yerine mevcut API'lerle tutarlı olması, uygulama geliştiricilere daha iyi bir kullanıcı deneyimi sağlayabilir.
API ile ilgili çözülmesi gereken zor sorular veya güncellenmesi gereken yönergeler varsa kendi değerlendirmenizi yapın ve API Konseyi ile iletişime geçin.
API'nin temel özellikleri
Bu kategori, bir Android API'sinin temel yönleriyle ilgilidir.
Tüm API'ler uygulanmalıdır
Bir API'nin kitlesinden (ör. herkese açık veya @SystemApi
) bağımsız olarak, birleştirildiğinde veya API olarak kullanıma sunulduğunda tüm API yüzeyleri uygulanmalıdır. API saplarını daha sonra uygulanacak olanlarla birleştirmeyin.
Uygulaması 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 kullanabilmesi için uygun API'lere sahip olup olmadığını doğrulamanın bir yolu yoktur.
- Uygulaması olmayan API'ler, Developer Preview'larda test edilemez.
- Uygulaması olmayan API'ler CTS'de test edilemez.
Tüm API'ler test edilmelidir.
Bu, platform CTS şartları, AndroidX politikaları ve genel olarak API'lerin uygulanması gerektiği fikriyle uyumludur.
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ığı test etmek 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 semantiği, 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 son değer sınıfları veya son 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
sınıfının yeni bir genel 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 devralma işlemi yapı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 durumsuz olduğu durumlarda API tasarımcıları, soyut sınıflar yerine arayüzleri tercih etmelidir. Yani varsayılan arayüz yöntemleri, diğer arayüz yöntemlerine yapılan çağrılar olarak uygulanabilir.
Varsayılan 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 öğeleri 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
View
içerir.
Örneğin, geri bağlantı ipuçları için View
ile ilişkili durumun kalıcı olması ve geri bağlantıyı 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 olmayan diğer API yüzeyleri dahildir. Ancak Android'deki IDL'nin çoğu AIDL'dedir. Bu nedenle bu sayfada AIDL'ye odaklanılmıştır.
Oluşturulan AIDL sınıfları, API stil kılavuzu şartlarını karşılamaz (ör. aşırı yükleme kullanamazlar) ve AIDL aracı, dil API'si uyumluluğunu koruyacak şekilde 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.
Bağlayıcı 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ış kullanma, paylaşılan bellek kullanma gibi 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 herkese açık bir API olarak kullanmayı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ü gerekliliği, 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ı herkese açık API'yi kullanmayı kolaylaştırmak amacıyla yine de bir sarmalayıcı katman kullanmak faydalı olacaktır.
Herkese açık API'de ham Binder nesneleri kullanmayın
Binder
nesnesi kendi başına bir anlam ifade etmediğinden herkese açık 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 mutasyonuna 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 eşzamansız kodla kullanılması zordur.
Platform kodunda ve hem Kotlin hem de Java tarafından kullanılan düşük düzeyli kitaplık API'lerinde, tamamlama geri çağırma işlevi, Executor
ve API iptali destekliyorsa CancellationSignal
kombinasyonunu tercih edin.
public void asyncLoadFoo(android.os.CancellationSignal cancellationSignal,
Executor callbackExecutor,
android.os.OutcomeReceiver<FooResult, Throwable> callback);
Kotlin'i hedefliyorsanız suspend
işlevlerini tercih edin.
suspend fun asyncLoadFoo(): Foo
Java'ya özgü entegrasyon kitaplıklarında Guava'yı kullanabilirsiniz
ListenableFuture
.
public com.google.common.util.concurrent.ListenableFuture<Foo> asyncLoadFoo();
İsteğe bağlı parametreleri 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
'yi gereksiz hale getirir.
İsteğe bağlı temel öğeler için eşlenmiş has
ve get
yöntemlerini kullanın. Değer ayarlanmamışsa (has
, false
değerini döndürürse) get
yöntemi bir IllegalStateException
oluşturmalıdır.
public boolean hasAzimuth() { ... }
public int getAzimuth() {
if (!hasAzimuth()) {
throw new IllegalStateException("azimuth is not set");
}
return azimuth;
}
Örnek oluşturulamayan 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 örneği oluşturulamayan sınıflar, varsayılan bağımsız değişken içermeyen oluşturucu kullanılarak örnek oluşturulmasını ö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 dezavantajlara sahip oldukları için önerilmez:
- İnşaat, sınıf tarafından yönetilir ve sahte ürünlerin kullanılmasını önler.
- Tekil nesnenin 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 kalıbını 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, tekil örnekten farklı olarak geliştiricilerin SingleInstance
öğesinin sahte bir sürümünü oluşturabilmesi ve kendi bağımlılık ekleme çerçevelerini kullanarak uygulamayı yönetebilmesi açısından farklıdır. Bu durumda, sarmalayıcı oluşturmaları gerekmez veya kitaplık, -testing
yapısında kendi sahte sürümünü sağlayabilir.
Kaynakları serbest bırakan sınıflar AutoCloseable'ı uygulamalıdır.
close
, release
, destroy
veya benzeri yöntemlerle kaynak yayınlayan sınıflar, geliştiricilerin try-with-resources
bloğu kullanırken bu kaynakları otomatik olarak temizlemesine olanak tanımak için java.lang.AutoCloseable
yöntemini uygulamalıdır.
android.* içinde yeni View alt sınıfları kullanmaktan kaçının.
Platformun 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. Yeni platform tarafından sunulan kullanıcı arayüzü özellikleri, Jetpack Compose'u ve isteğe bağlı olarak View tabanlı kullanıcı arayüzü bileşenlerini uygulamak için kullanılabilecek daha düşük seviyeli API'ler olarak sunulmalıdır. Jetpack kitaplıklarındaki geliştiriciler için. Bu bileşenlerin kitaplıklarda sunulması, platform özellikleri kullanılamadığında geriye dönük bağlantı uygulamaları için fırsatlar sunar.
Fields'ın oynadığı filmler
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 durumun nadir istisnalarıdı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şkene veya sabite flag
adını vermeyin.
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, tamamen büyük harflerle ve alt çizgiyle ayrılmış adlandırma kuralını 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 kullanma
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ıyla tutarlı olmalı ve genellikle paket veya alanla sınırlı olmalıdır. Örneğin:
public static final String FOO_THING = "foo"
tutarlı bir şekilde adlandırılmamış veya uygun şekilde kapsamlandırılmamış. Bunun yerine şunları 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, camelCase adlandırma kuralı kullanılarak adlandırılmalıdır. Örneğin, @id/accessibilityActionPageUp
veya @attr/textAppearance
.
Bazı durumlarda, herkese açık tanımlayıcı veya özellik, alt çizgiyle ayrılmış ortak bir önek içerir:
- config.xml dosyasında
@string/config_recentsComponentName
gibi platform yapılandırma değerleri - attrs.xml dosyasındaki
@attr/layout_marginStart
gibi 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ılması gerekiyorsa herkese açık düzenler ve çizilebilir öğeler, alt_çizgi 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 ileriye dönük uyumluluğu göz önünde bulundurun
Gelecekteki API sürümlerinde tanımlanan sabitler, eski API'leri hedefleyen uygulamalar tarafından bilinmez. Bu nedenle, uygulamalara 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ş. Ancak uygulama, yeni eklenen STATUS_FAILURE_RETRY
öğesini aldığında bunu başarı olarak yorumlar.
Sabit değerler 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ı olarak kendi targetSdkVersion
değerlerini belirleyemez ve kitaplık kodundaki targetSdkVersion
davranış değişikliklerinin işlenmesi 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 bir oluşturucu sağlamalı veya çok sayıda özellik olduğunda Builder
kalıbını kullanmalıdır.
Kotlin'de veri sınıfları, özellik 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ına uygun, ö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" öğesi, 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öntemlerde birim adları için kısaltılmamış kısa biçim tercih edilmelidir.
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ü bir yazım sağlamak üzere çeşitli ek açıklamalar içerir:
@CurrentTimeMillisLong
: Değer,1970-01-01T00:00:00Z
tarihinden 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: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.
Ölçü birimleri
Zaman dışında bir ölçü birimini ifade eden tüm yöntemler için CamelCased SI birim önekleri tercih edilir.
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 temel olarak farklı bir şey yapıyorsa 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 yapan bir işlemdir.
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. İşaretleri ve diğer seçenekleri belirten parametreler, üzerinde işlem yapılan nesneyi açıklayan parametrelerden daha az ö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
İnşaat Ustaları
Builder deseni, karmaşık Java nesneleri oluşturmak için önerilir ve Android'de şu durumlarda yaygın olarak kullanılır:
- 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 parametrelerinden yalnızca birkaçını yapılandırma
- Bazen benzer veya eşleşen türlerde olan birçok farklı isteğe bağlı ya da zorunlu oluşturma parametresini yapılandırın. Bu sayede, aksi takdirde okunması kafa karıştırıcı veya yazılması hataya açık olabilecek çağrı siteleri oluşturabilirsiniz.
- Bir nesnenin artımlı olarak oluşturulmasını yapılandırın. Burada, yapılandırma kodunun farklı parçaları, 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 verin.
Üç 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 düz 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çebilirler.
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 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 bir oluşturucu aracılığıyla oluşturulmalı ve statik oluşturucu yöntemiyle oluşturulmamalı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, @PublishedApi internal
kullanarak Builder
sınıf oluşturucusunu Kotlin istemcilerinden seçmeli olarak gizlememelidir.
public class Tone {
public static Builder builder();
public static class Builder {
}
}
public class Tone {
public static class Builder {
public Builder();
}
}
Oluşturucu yapıcılara 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ürlerin 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ır.
public class Tone {
public static class Builder {
public Builder clone();
}
public Builder toBuilder();
}
public class Tone {
public static class Builder {
public Builder(Builder original);
public Builder(Tone original);
}
}
Oluşturucuda kopya oluşturucu varsa oluşturucu ayarlayıcılar @Nullable bağımsız değişkenler almalıdır.
Bir oluşturucunun yeni bir ö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şkenlerini alabilir.
İkinci derece giriş için null değer kullanmak genellikle daha basittir. Özellikle de oluşturucular ve aşırı yüklemeler yerine varsayılan bağımsız değişkenleri kullanan Kotlin'de bu durum geçerlidir.
Ayrıca, @Nullable
ayarlayıcılar bunları alıcılarıyla eşleştirir. İsteğe bağlı özellikler için @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
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.
Builder build() yöntemleri @NonNull nesneler döndürmelidir
Bir oluşturucunun build()
yönteminin, oluşturulan nesnenin null olmayan bir örneğini döndürmesi beklenir. Nesne, geçersiz parametreler nedeniyle oluşturulamadığında 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 geliştiricilerin erişim yöntemi davranışıyla ilgili 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.
compute
tercih 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/Ç).
fetch
tercih edilir. - Yöntem, bir değer döndürene kadar iş parçacığını engeller.
await
tercih edilir. - Yöntem, her çağrıda yeni bir nesne örneği döndürür.
create
tercih edilir. - Yöntem, değeri başarıyla döndürmeyebilir.
request
tercih 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 olarak 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 beyanı 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 alıcı yöntemi adına get
eklenmez, alıcı olarak özellik adı kullanılır.
Bu nedenle, adlandırma yönergesine uymak için Boolean
özelliklerini is
önekiyle 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 ayarlayıcı yöntem sağlanmalıdır: biri tam bir bit dizesi alan ve mevcut tüm işaretleri ü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 elde etmek için bir getter 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 edin. 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.
protected
Gö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 herkese açık API'si aracılığıyla da edinilebilmelidir. Aksi takdirde, geliştiricileri toString()
çıktınızı ayrıştırmaya ve kullanmaya teşvik etmiş olursunuz. Bu da gelecekteki değişiklikleri engeller. İyi bir uygulama, toString()
işlevini yalnızca nesnenin herkese açık API'sini kullanarak uygulamaktır.
Hata ayıklama çıkışına güvenmeyi engelleme
Geliştiricilerin hata ayıklama çıkışına bağlı olması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();
}
Dosya nesnelerini kabul eden yöntemler, akışları da kabul etmelidir.
Android'deki veri depolama konumları her zaman diskteki dosyalar değildir. Örneğin, kullanıcı sınırları arasında aktarılan iç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 alınmış sürümler yerine ham temel öğeleri alma ve döndürme
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 ek 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 parametre ve dönüş değerlerini 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 geliştiricilere 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ça belirtilen nullability ek açıklamaları gerekir ancak nullability kavramı Kotlin dilinin bir parçasıdır ve nullability ek açıklamaları Kotlin API'lerinde asla kullanılmamalıdır.
@Nullable
: Belirli bir dönüş değerinin, parametrenin veya alanın boş 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 üç durum söz konusudur. 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 bir doküman otomatik olarak 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, boş değer 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 bu yöntem writeToParcel(@NonNull Parcel,
int)
olmalı, writeToParcel(Parcel, int)
olmamalıdır. Ancak ek açıklamaları eksik olan mevcut API'lerin "düzeltilmesi" gerekmez.
Boş değer atanabilirliği zorunluluğu
Java'da, @NonNull
parametreleri için giriş doğrulaması yapmak üzere Objects.requireNonNull()
kullanılması ve parametreler boş 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çıklanmalıdır.
Tümünü yakalama @AnyRes
'ın yanı sıra @StringRes
, @ColorRes
ve @AnimRes
gibi her kaynak türü için bir açıklama bulunur. Örneğin:
public void setTitle(@StringRes int resId)
Sabit kümeler için @IntDef
Sihirli sabitler: String
ve int
parametreleri, herkese açık sabitlerle gösterilen 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
oluşturmak için yöntemler önerilir.
@IntDef
Bitmask 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: Genel alanlar şu SdkConstant
değerlerden birini aldığında ACTIVITY_INTENT_ACTION
, BROADCAST_INTENT_ACTION
, SERVICE_ACTION
,
INTENT_CATEGORY
, FEATURE
olarak ekleyin.
@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 | Çocuğum için |
---|---|---|
Dönüş türü | Notlandırılmamış | Açıklama eklenmemiş veya boş olmayan |
Dönüş türü | Boş değer atanabilir | Boş değer atanabilir veya boş değer atanabilir olmayan |
Dönüş türü | Nonnull | Nonnull |
Eğlenceli tartışma | Notlandırılmamış | Açıklama eklenmemiş veya boş değer atanabilir |
Eğlenceli tartışma | Boş değer atanabilir | Boş değer atanabilir |
Eğlenceli tartışma | Nonnull | Boş değer atanabilir veya boş değer atanabilir olmayan |
Mümkün olduğunda null değer içermeyen (ö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 atanamayan (ör. @NonNull) dönüş türlerini tercih edin.
Bundle
veya Collection
gibi kapsayıcı türleri için boş bir kapsayıcı döndürün. Bu kapsayıcı, geçerli olduğu durumlarda değiştirilemez olmalıdır. null
öğesinin bir kapsayıcının kullanılabilirliğini ayırt etmek için kullanılacağı 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ı konusunda her zaman aynı fikirde 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 kaynakta değişiklik yapılmasına neden olan bir durumdur.
@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 değerleri 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.
@IntDef
için bu,OTHER
veyaUNKNOWN
değerinin eklenmesi anlamına gelir. Yeni bir kod döndürürken, uygulamanın bilmediği bir hata kodu döndürmemek için arayanıntargetSdkVersion
değerini kontrol edebilirsiniz. İstisnalar için, istisnalarınızın uyguladığı ortak bir üst sınıfı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 yoksayması zor veya imkansız olmalıdır. Hatayı bir değer döndürerek bildiriyorsanız yönteminize
@CheckResult
ekleyin.
Geliştiricinin yanlış yaptığı bir şey nedeniyle (ör. giriş parametrelerindeki kısıtlamaları yoksayma veya gözlemlenebilir durumu kontrol etmeme) bir hata veya hata koşuluna ulaşıldığında ? extends RuntimeException
atmayı tercih edin.
Setter veya işlem (örneğin, 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
alan olarak tanımlanmalı, ERROR_
ile öneklenmeli ve @hide
@IntDef
notunda 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 çeşitli avantajlar sunar. Bunlar arasında benzersizlik ve sıralama konusunda daha güçlü API sözleşmeleri, genel türler için destek ve geliştiriciler için kolaylık sağlayan çeşitli yöntemler yer alır.
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şası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 şekilde 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
öğesini 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 varsayılan olarak koleksiyonlar için 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.empty
dönüş türleridir. Değiştirilebilirliğ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 uyulmalı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ştirilebilirliğ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önetme konusunda son derece 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, bir alanda veya anonim iç sınıfta kalıcı hale getirilecek durumlarda dizi parametresinin savunma amaçlı bir sığ kopyasını oluşturmaları ş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 semantik 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 yinelenenlere izin veriyorsa
Collection<Foo>,
.
Kotlin dönüşüm işlevleri
Kotlin, .toFoo()
ve .asFoo()
işlevlerini kullanarak mevcut bir nesneden farklı türde bir nesne elde etmek için sıkça kullanılı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 yayınlama 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 herkese açık 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 oluşturma
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.
Herkese açık olarak çağrılan yönteme doğrudan sağlanan bağımsız değişkenlerle ilgili olmayan hatalar, java.lang.IllegalArgumentException
veya java.lang.NullPointerException
yerine java.lang.IllegalStateException
değerini döndürmelidir.
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 arama yöntemleri, etkinliğin gerçekleşip gerçekleşmediğini veya gerçekleşme sürecinde olup olmadığını belirtecek şekilde adlandırılmalıdır.
Örneğin, 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 dinleyici veya geri çağırma eklenebildiğinde ya da nesneden dinleyici 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ğırma işlevlerini kaydetme veya kaydını silme işlemlerini içeren yöntemlerde, geri çağırma işlevi türünün tam adı belirtilmelidir.
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ğırmalar 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 çözümdür 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()))
'yı arar. - A geliştiricisi, geri çağırmasını
a
kaldırmak istiyor ancakB
tü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 etme
Açıkça iş parçacığı 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 ekler. 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 gerçekleştirilebilir.
Trambolin kelimesine benzer bir HandlerThread
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
, 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ığı oluyor. Geliştiricilere mevcut/tercih edilen yürütme bağlamlarını yeniden kullanmak için ihtiyaç duydukları kontrolü sağlamak amacıyla Executor
kullanılmalıdır.
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 kuralın 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 talebi 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 durumunu etkilemez. Bu konuda doğru yaklaşımı benimseyen veya yaygın 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öntemin yanı sıra mevcut Handler
tabanlı yöntemleri kullanma olasılığına bağlı olarak yeni bir Handler
tabanlı yöntem eklemek için istisna verilebilir.
Kayıtta simetri
Bir şeyi eklemenin veya kaydetmenin bir yolu varsa kaldırmanın/kaydını silmenin de bir yolu olmalıdır. Yöntem
registerThing(Thing)
eşleşen bir
unregisterThing(Thing)
İstek tanımlayıcısı sağlama
Geliştiricinin geri 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 fazla yöntem içeren 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öntemleri olmadığı için 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 kullanma
OutcomeReceiver<R,E>
başarılı olduğunda R
, aksi takdirde E : Throwable
sonuç değerini bildirir. Bu, düz 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 asenkron yönteme dönüştürürken geri çağırma türü olarak OutcomeReceiver
kullanın:
interface FooType {
// Before:
public FooResult requestFoo(FooRequest request);
// After:
public void requestFooAsync(FooRequest request, Executor executor,
OutcomeReceiver<FooResult, Throwable> callback);
}
Bu şekilde dönüştürülen eşzamansız yöntemler her zaman void
döndürür. requestFoo
döndürecek tüm sonuçlar, sağlanan executor
üzerinde çağrı yapılarak requestFooAsync
'nin callback
parametresine OutcomeReceiver.onResult
olarak 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 işlev çağrısının rahatlığıyla engellemeyen asenkron 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
yapısının 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.
İşi tamamlandığında sonuç döndüren veya istisna atan bir yöntemin semantiğine uymayan asenkron 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 ve Android API yüzey alanını gereksiz yere genişletir.
Yeni arayüzler oluşturmak yerine şu genel arayüzleri kullanmayı düşünebilirsiniz:
Runnable
:() -> Unit
Supplier<R>
:() -> R
Consumer<T>
:(T) -> Unit
Function<T,R>
:(T) -> R
Predicate<T>
:(T) -> Boolean
- Referans 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ın etkinleştirilmesi 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 şeyi döndüren yöntemlerde @return
öğesini atlayabilir ve şuna benzer dokümanlar yazabilirsiniz:
/**
* Returns the priority of the thread.
*/
@IntRange(from = 1, to = 10)
public int getPriority() { ... }
Javadoc'da her zaman bağlantı kullan
Dokümanlar, ilgili sabitler, yöntemler ve diğer öğeler için diğer dokümanlara bağlanmalıdır. 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çıklama kullanmanın, izin verilen türleri belirten belgeleri otomatik olarak oluşturduğunu unutmayın. IntDef
hakkında daha fazla bilgi için notlarla ilgili yönergelere bakın.
Javadoc eklerken update-api veya docs hedefini çalıştırma
Bu kural, özellikle @link
veya @see
etiketleri eklerken önemlidir. Çıkışın beklendiği gibi göründüğünden emin olun. Javadoc'teki ERROR çıkışı genellikle kötü bağlantılardan kaynaklanır. Bu kontrolü update-api
veya docs
hedefi yapar. 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 herkese açık API'den gizlendiğini belgeleyin.
@deprecated
notuyla işaretlenmiş API öğelerinin nasıl değiştirileceğine ilişkin 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 kontrol edilmemiş bir istisna (ör. IllegalArgumentException
veya IllegalStateException
) oluşturursa istisnayı, neden oluşturulduğunu açıklayarak belgeleyin. Oluşan istisna, neden oluştuğunu da belirtmelidir.
Belirli 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 @IntDef
veya benzer bir ek açıklamayla eşleşmediği NullPointerException
ya da IllegalArgumentException
:
/**
* ...
* @throws IOException If it cannot find the schema for {@code toVersion}
* @throws IllegalStateException If the schema validation fails
*/
public SupportSQLiteDatabase runMigrationsAndValidate(String name, int version,
boolean validateDroppedTables, Migration... migrations) throws IOException {
// ...
if (!dbPath.exists()) {
throw new IllegalStateException("Cannot find the database file for " + name
+ ". Before calling runMigrations, you must first create the database "
+ "using createDatabase.");
}
// ...
Kotlin'de ise:
/**
* ...
* @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, istisnalar oluşturabilecek eşzamansız 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.attr
dokümanlarındakiactionBarTabStyle
simgesine bakın. - Aynı nedenle ilk cümlede "ör." ifadesini kullanmayın. Çünkü Doclava, "ör." ifadesinden sonra sinopsis belgelerini sonlandırır. Örneğin,
View.java
bölümündekiTEXT_ALIGNMENT_CENTER
konusuna 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 kapatma</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, köşeli parantezleri<>
dikkatli kullanın.<
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ı Nasıl Yazılır?) 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ı ile 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
Keyfi 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şturucusu ö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ırmaları yalnızca yeni değerler eklenmeyeceğinden emin olduğunuzda kullanın.
IntDef
avantajları:
- Zaman içinde değer eklemeyi sağlar.
- Kotlin
when
ifadeleri, 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,
when
deyiminin 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
when
ifadesi
- Ö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
uzantısı kullanıyorsa 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 herkese açık 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 standart kod, 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'yu kullanan 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() kullanarak 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 herkese açık API için uygun değildir. Değiştirilebilir, yüksek sıklıkta yöntem çağrıları için ayırma gerektirir ve her bitin neyi temsil ettiğine dair anlamsal 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. Ham ikili veriler için byte[]
kullanın.
android.net.Uri tercih edilir
android.net.Uri
, Android API'lerindeki URI'ler için tercih edilen kapsülleme yöntemidir.
URI'leri ayrıştırmada 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 ek açıklamalar, bir API'ye iletilebilecek bir dizi geçerli sabiti ifade eder. Ancak API olarak dışa aktarıldıklarında derleyici, sabitleri satır içi olarak ekler ve yalnızca (artık işe yaramayan) değerler, açıklamanın API saplamasında (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
'dan yeni anahtarlar göstermeyin.
Bunun yerine, ilgili sınıfa (genellikle bir "yönetici" sınıfı) uygun bir getter ve setter Java API'si ekleyin. Gerektiğinde 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:
- Tür 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()
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'sini 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()
'dan 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ı kimliklerini uid'lerle karıştırmamak 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 aşağıda belirtilen endişeler yer almaktadır:
FLAG_RECEIVER_REGISTERED_ONLY
iş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çinJobScheduler
gibi 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 gizlilik endişelerine 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 hale getirir.
Yayın kuyrukları paylaşılan bir kaynak olduğundan aşırı yüklenebilir ve etkinliğinizin zamanında yayınlanmamasına neden olabilir. 10 dakika veya daha uzun uçtan uca gecikmeye sahip 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ı kullanmasını ö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_ONLY
kullanarak yayınınızı halihazırda çalışan uygulamalarla sınırlayın. Örneğin,ACTION_SCREEN_ON
, uygulamaları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_INTERFACE
dize sabiti tanımlayın. Bu sabit,@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
ile açıklama eklenmelidir. - Bir geliştiricinin platformdan niyet almak için
<intent-filter>
eklemesi gereken sınıf hakkındaki doküman.AndroidManifest.xml
- Kötü amaçlı uygulamaların geliştirici hizmetlerine
Intent
gö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. Çünkü 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 kullanıma sunmak 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ığı gibi ç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 bu değişiklikleri mevcut uygulamalar ve kod tabanlarıyla uyumluluğu en üst düzeye çıkaracak şekilde nasıl uygulamanız gerektiğiyle ilgili politikalar açıklanmaktadır.
İkili uyumluluğu bozan değişiklikler
Sonlandırılmış herkese açık 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. Şüphe duyduğunuzda, 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'lerin geliştirilmesi) 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 sınıf ekleme 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östermelisiniz.
Bazı durumlarda, geliştirici deneyimini veya kod doğruluğunu iyileştirmek için kaynakta değişiklik yapılması gerekir. Örneğin, Java kaynaklarına 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
@Deprecated
ile işaretleyin. - Değiştirme işlemleri ekleyin ve
@deprecated
docs ek açıklamasını kullanarak, Javadoc'ta kullanımdan kaldırılan kodun yerine geçen kodun bağlantısını verin. - Geliştirme döngüsü sırasında, API'nin kullanımdan kaldırılacağını belirterek kurum içi kullanıcılara karşı hataları dosyalayın. Bu, değiştirilen API'lerin yeterli olduğunu doğrulamaya yardımcı olur.
- Kodu
- API y+2 - Geçici kaldırma
- Kodu
@removed
ile işaretleyin. - İsteğe bağlı olarak, yayın için mevcut SDK düzeyini hedefleyen uygulamalarda istisna oluşturun veya hiçbir işlem yapmayın.
- 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ı, 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 kullanılabilecek 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 verilebilir özellikler de dahil olmak üzere XML'de tanımlanan ve Java'da kullanıma sunulan API'leri kullanımdan kaldırmanız ve bir özetle birlikte göndermeniz gerekir:
<!-- 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'lerin 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,
-Werror
kullanan 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.com
ile ilgili dokümanlarda desteğin sonlandırılacağı bildiriliyor.- Android Studio gibi IDE'ler, API kullanım sitesinde uyarı gösterir.
- IDE'ler, API'yi otomatik tamamlama özelliğinde daha alt sıralara yerleştirebilir veya gizleyebilir.
Sonuç olarak, bir API'nin desteğinin sonlandırılması, kod sağlığıyla en çok ilgilenen geliştiricilerin (-Werror
kullananlar) yeni SDK'ları 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, her 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
@remove
planlı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 yerine yeni bir API koyduğunuzda, 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 kullanımdan kaldırılan API'ler varsa (bu nedenle başlangıçta herkese açık API yüzeyine kullanımdan kaldı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çınılmalıdır.
Sistem API'leri için, API'yi geçici olarak kaldırmadan önce büyük bir sürüm boyunca kullanımdan kaldı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. Desteğin Sonlandırılması başlıklı makalede açıkladığımız gibi, özetinizde 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 API'yi çağırırken kilitlenmeyecek şekilde 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. Yumuşak bir şekilde 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 saplama 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ştiricisi 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 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ıyla genişletmesi mümkün olmaz.
Geliştiricilerin bir sınıfı genişletmesinin hiçbir zaman ve asla mümkün olmadığı 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ılacak şekilde geçiş yaptı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 hata ayıklama sürecinde, önizleme ve beta sürümleri sırasında belirli davranış değişikliklerini 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
İleriye dönük 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 hem ilk tasarıma hem de gelecekteki değişikliklere özellikle dikkat etmeniz gerekir.
Aşağıdakiler, Android'de en sık karşılaşılan ileri uyumluluk sorunlarına neden olur:
- Daha önce tamamlanmış olduğu varsayılan bir kümeye (ör.
@IntDef
veyaenum
) 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'deColorStateList
tü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 durumu sahada bulunan 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 yalnızca bu sürümde 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 işletim sisteminin 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 modeli
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>FrameworkInitializer
sınıfını kullanıma sunun. Bu sınıfın$BOOTCLASSPATH
içinde olması gerekir. Örnek: StatsFrameworkInitializer@SystemApi(client = MODULE_LIBRARIES)
ile işaretleyin.public static void registerServiceWrappers()
yöntemi ekleyin.SystemServiceRegistry.registerContextAwareService()
öğesine referans gerektiğinde bir hizmet yöneticisi sınıfı kaydetmek içinContext
öğesini kullanın.Bir
SystemServiceRegistry.registerStaticService()
sınıfına referans gerekmediğinde hizmet yöneticisi sınıfı kaydetmek içinContext
kullanın.SystemServiceRegistry
'nin 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 sistem hizmeti 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 bu hizmetlere referans alabilmek için bunun yerine aşağıdaki kalıbı kullanabilir.
TelephonyServiceManager tasarımını izleyerek
<YourModule>ServiceManager
sınıfı oluşturun.Sınıfı
@SystemApi
olarak kullanıma sunun. Yalnızca$BOOTCLASSPATH
sınıflarından veya sistem sunucusu sınıflarından erişmeniz gerekiyorsa@SystemApi(client = MODULE_LIBRARIES)
kullanabilirsiniz. Aksi takdirde@SystemApi(client = PRIVILEGED_APPS)
işe yarar.Bu sınıf ş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ı yönteminiz olmalıdı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 alanFrameworkInitializer
sınıfınıza statik bir@SystemApi(client = MODULE_LIBRARIES)
API'si eklersiniz.
Bu kalıp, diğer ana hat modüllerinin <YourModule>ServiceManager
API'lerinin bir örneğini almasının bir yolu olmadığı için bu API'lere erişmesini engeller. get()
ve register()
API'leri diğer modüller tarafından görülebilir olsa da bu durum geçerlidir.
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 modüllere 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
için register()
yöntemi 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 tanımlayabilir.AndroidManifest.xml
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ı durumunda 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ülden gelen 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
.