Android API yönergeleri

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:

  1. İnşaat, sınıf tarafından yönetilir ve sahte ürünlerin kullanılmasını önler.
  2. Tekil nesnenin statik yapısı nedeniyle testler hermetik olamaz.
  3. 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:

  1. 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());
    
  2. 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.

protectedGörünürlüğün geliştiricilerin bir API'yi çağırmasını engellemediğini, yalnızca biraz daha rahatsız edici hale getirdiğini unutmayın.

equals() ve hashCode() yöntemlerinden hiçbirini veya her ikisini de uygulamayın.

Birini geçersiz kılarsanız diğerini de geçersiz kılmanız gerekir.

Veri sınıfları için toString() işlevini uygulama

Veri sınıflarının, geliştiricilerin kodlarında hata ayıklamasına yardımcı olmak için toString() işlevini geçersiz kılması önerilir.

Çıkışın program davranışı mı yoksa hata ayıklama için mi olduğunu belgeleyin.

Program davranışının uygulamanıza bağlı olmasını isteyip istemediğinize karar verin. Örneğin, UUID.toString() ve File.toString(), programların kullanması için kendi biçimlerini belgeler. Yalnızca hata ayıklama amacıyla Intent gibi bilgileri kullanıma sunuyorsanız üst sınıftaki dokümanları devraldığınızı belirtin.

Ek bilgi eklemeyin

toString()'dan edinilebilen tüm bilgiler, nesnenin 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?

  1. 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.
  2. 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 veya UNKNOWN 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ın targetSdkVersion 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.
  3. 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.emptydö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 ancak B 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)

İsteğe bağlı parametrelerle ilgili normal ExecutorExecutor 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:

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() { ... }

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ındaki actionBarTabStyle 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ündeki TEXT_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 etikette class="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. &lt; ve &gt; 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.
  • Ç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
  • 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çin JobScheduler 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() veya Intent.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:

  1. 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.
  2. Bir geliştiricinin platformdan niyet almak için <intent-filter> eklemesi gereken sınıf hakkındaki doküman.AndroidManifest.xml
  3. 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.
  • 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.
  • 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.

Ayrıca, API'leri @deprecated olarak işaretlemenizi de zorunlu tutuyoruz. Ancak bu, geliştiricileri halihazırda kullandıkları bir API'den taşınmaya teşvik etmek için yeterli bir motivasyon sağlamıyor.@removed

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.
  • 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 veya enum) yeni sabitler ekleme (ör. switch'nin bir istisna oluşturan default'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'de ColorStateList 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çin Context öğesini kullanın.

  • Bir SystemServiceRegistry.registerStaticService() sınıfına referans gerekmediğinde hizmet yöneticisi sınıfı kaydetmek için Context kullanın.

  • SystemServiceRegistry'nin statik başlatıcısından registerServiceWrappers() 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 alan FrameworkInitializer 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.