Veri Deposu Android Jetpack'in bir parçasıdır.

Jetpack DataStore, anahtar/değer çiftlerini depolamanıza olanak tanıyan bir veri depolama çözümüdür. protokol ile eşlenen veya yazılan nesne tamponlar ekleyin. DataStore, Kotlin kullanıyor eş yordamlar ve Akış, verileri eşzamansız, tutarlı ve sağlayabilir.

Mevcut bir SharedPreferences - veri depoluyorsanız bunun yerine DataStore'a taşımayı düşünebilirsiniz.

DataStore ve Proto DataStore tercihleri

DataStore, iki farklı uygulama sunar: Tercihler DataStore ve Proto DataStore.

  • Tercihler DataStore, verileri depolar ve anahtarları kullanarak verilere erişir. Bu uygulama, önceden tanımlanmış bir şema gerektirmez ve yazın.
  • Proto DataStore, verileri özel bir veri türünün örnekleri olarak depolar. Bu uygulama, protokolü kullanarak bir şema tanımlamanızı gerektirir: arabelleklere eklenir, ancak tür yardımcı olur.

DataStore'u doğru şekilde kullanma

DataStore'u doğru şekilde kullanmak için aşağıdaki kurallara her zaman dikkat edin:

  1. Belirli bir dosya için hiçbir zaman DataStore öğesinin birden fazla örneğini oluşturma gerekir. Aksi takdirde DataStore işlevselliği bozulabilir. Varsa işlemde belirli bir dosya için birden çok DataStore etkinse DataStore, verileri okurken veya güncellerken IllegalStateException komutunu çalıştırın.

  2. DataStore'un genel türü sabit olmalıdır. Bir türü değiştirme DataStore'da kullanılanlar, DataStore'un sağladığı ve oluşturduğu tüm garantileri geçersiz kılar yakalanması zor hatalar belirlemelisiniz. Optimum kampanya performansı için sabitlik garantileri, basit bir API ve verimli bir serileştirme sürecidir.

  3. SingleProcessDataStore ve MultiProcessDataStore kullanımlarını hiçbir zaman karıştırma oluşturduğunuzdan emin olun. DataStore hizmetine birden fazla cihazdan erişmeyi düşünüyorsanız işlemi için her zaman MultiProcessDataStore kullanın.

Kurulum

Uygulamanızda Jetpack DataStore'u kullanmak için aşağıdakini Gradle dosyanıza ekleyin bağlı olarak şunları yapabilirsiniz:

Tercihler Veri Deposu

Eski

    // Preferences DataStore (SharedPreferences like APIs)
    dependencies {
        implementation "androidx.datastore:datastore-preferences:1.1.1"

        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-preferences-rxjava2:1.1.1"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-preferences-rxjava3:1.1.1"
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation "androidx.datastore:datastore-preferences-core:1.1.1"
    }
    

Kotlin

    // Preferences DataStore (SharedPreferences like APIs)
    dependencies {
        implementation("androidx.datastore:datastore-preferences:1.1.1")

        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-preferences-rxjava2:1.1.1")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.1")
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation("androidx.datastore:datastore-preferences-core:1.1.1")
    }
    

Proto DataStore

Eski

    // Typed DataStore (Typed API surface, such as Proto)
    dependencies {
        implementation "androidx.datastore:datastore:1.1.1"

        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-rxjava2:1.1.1"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-rxjava3:1.1.1"
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation "androidx.datastore:datastore-core:1.1.1"
    }
    

Kotlin

    // Typed DataStore (Typed API surface, such as Proto)
    dependencies {
        implementation("androidx.datastore:datastore:1.1.1")

        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-rxjava2:1.1.1")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-rxjava3:1.1.1")
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation("androidx.datastore:datastore-core:1.1.1")
    }
    
.

Anahtar/değer çiftlerini Preferences DataStore ile depolama

Tercihler DataStore uygulaması, DataStore ve Preferences sınıfları kullanmanızı öneririz.

Tercihler DataStore oluşturma

Datastore<Preferences> örneği oluşturmak için preferencesDataStore tarafından oluşturulan mülk yetkilendirmesini kullanın. Kotlin dosyanızın en üst düzeyinde bir kez çağırıp uygulamanızın geri kalanında bu mülk üzerinden bu koda erişin. Bu sayede, DataStore verilerinizi daha kolay bir şekilde tek bir yerde tutabilirsiniz. Alternatif olarak RxPreferenceDataStoreBuilder da kullanılabilir. veya RxJava kullanıyorsanız. Zorunlu name parametresi Tercihler DataStore.

Kotlin

// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

Java

RxDataStore<Preferences> dataStore =
  new RxPreferenceDataStoreBuilder(context, /*name=*/ "settings").build();

Tercihler DataStore'dan okuma

Tercihler DataStore önceden tanımlanmış bir şema kullanmadığı için her değer için bir anahtar tanımlamak üzere o değere karşılık gelen anahtar türü DataStore<Preferences> örneğinde depolayacağım. Örneğin, bir anahtarı tanımlamak için bir tam sayı değeri için intPreferencesKey(). Daha sonra, DataStore.data mülkü için Flow kullanarak depolanan uygun değeri gösterir.

Kotlin

val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
val exampleCounterFlow: Flow<Int> = context.dataStore.data
  .map { preferences ->
    // No type safety.
    preferences[EXAMPLE_COUNTER] ?: 0
}

Java

Preferences.Key<Integer> EXAMPLE_COUNTER = PreferencesKeys.int("example_counter");

Flowable<Integer> exampleCounterFlow =
  dataStore.data().map(prefs -> prefs.get(EXAMPLE_COUNTER));

Tercihler DataStore'a yaz

Tercihler DataStore, edit() işlevi, DataStore içindeki verileri işlemsel olarak güncelleyen işlevi kullanır. İşlevin transform parametresi, değerleri şu şekilde güncelleyebileceğiniz bir kod bloğunu kabul eder: gerekir. Dönüşüm bloğundaki kodun tamamı tek bir kod olarak kabul edilir belirtir.

Kotlin

suspend fun incrementCounter() {
  context.dataStore.edit { settings ->
    val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
    settings[EXAMPLE_COUNTER] = currentCounterValue + 1
  }
}

Java

Single<Preferences> updateResult =  dataStore.updateDataAsync(prefsIn -> {
  MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
  Integer currentInt = prefsIn.get(INTEGER_KEY);
  mutablePreferences.set(INTEGER_KEY, currentInt != null ? currentInt + 1 : 1);
  return Single.just(mutablePreferences);
});
// The update is completed once updateResult is completed.

Yazılan nesneleri Proto DataStore ile depolama

Proto DataStore uygulaması DataStore ve protokol süresini kullanır. arabellekleri kalıcı hale getirmek için anlamına gelir.

Şema tanımlama

Proto DataStore, app/src/main/proto/ dizini. Bu şema, nesnelerin türünü tanımlar proto DataStore'da kalıcı hale getirir. Proto tanımlama hakkında daha fazla bilgi edinmek için protobuf dili rehberini inceleyin.

syntax = "proto3";

option java_package = "com.example.application";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

Proto DataStore oluşturma

Yazdığınız verileri depolamak için Proto DataStore oluşturma işlemi iki adımdan oluşur: nesneler:

  1. Tanımlanan tür, T olan Serializer<T> yöntemini uygulayan bir sınıf tanımlayın. emin olmanız gerekir. Bu serileştirici sınıfı DataStore'a okuma ve yazma konusunda bilgi verir veri türünüz. Serileştirici için varsayılan bir değer eklediğinizden emin olun: henüz bir dosya oluşturulmadıysa kullanılır.
  2. Örnek oluşturmak için dataStore tarafından oluşturulan mülk temsilcisini kullanın DataStore<T>; burada T, proto dosyasında tanımlanan türdür. Bunu ara bir kez kotlin dosyanızın en üst düzeyinde düzenleyebilir ve bu mülk aracılığıyla dosyaya erişebilirsiniz bu yetkiyi uygulamanızın tamamı boyunca kullanabilirsiniz. filename parametresi Verilerin depolanması için kullanılacak DataStore ve serializer parametresi DataStore'a serileştirici sınıfının adını söyler tanımlanmıştır.

Kotlin

object SettingsSerializer : Serializer<Settings> {
  override val defaultValue: Settings = Settings.getDefaultInstance()

  override suspend fun readFrom(input: InputStream): Settings {
    try {
      return Settings.parseFrom(input)
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException("Cannot read proto.", exception)
    }
  }

  override suspend fun writeTo(
    t: Settings,
    output: OutputStream) = t.writeTo(output)
}

val Context.settingsDataStore: DataStore<Settings> by dataStore(
  fileName = "settings.pb",
  serializer = SettingsSerializer
)

Java

private static class SettingsSerializer implements Serializer<Settings> {
  @Override
  public Settings getDefaultValue() {
    Settings.getDefaultInstance();
  }

  @Override
  public Settings readFrom(@NotNull InputStream input) {
    try {
      return Settings.parseFrom(input);
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException(“Cannot read proto.”, exception);
    }
  }

  @Override
  public void writeTo(Settings t, @NotNull OutputStream output) {
    t.writeTo(output);
  }
}

RxDataStore<Byte> dataStore =
    new RxDataStoreBuilder<Byte>(context, /* fileName= */ "settings.pb", new SettingsSerializer()).build();

Proto DataStore'dan okuma

Depolanan nesnenizdeki uygun özelliğin Flow öğesini göstermek için DataStore.data kullanın.

Kotlin

val exampleCounterFlow: Flow<Int> = context.settingsDataStore.data
  .map { settings ->
    // The exampleCounter property is generated from the proto schema.
    settings.exampleCounter
  }

Java

Flowable<Integer> exampleCounterFlow =
  dataStore.data().map(settings -> settings.getExampleCounter());

Proto DataStore'a yaz

Proto DataStore, bir updateData() depolanan bir nesneyi işlemsel olarak güncelleyen işlevdir. updateData() size veri türünüzün bir örneği olarak verilerin mevcut durumunu gösterir ve işlemsel olarak atomik bir okuma-yazma-değiştirme işleminde verilerdir.

Kotlin

suspend fun incrementCounter() {
  context.settingsDataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
      .setExampleCounter(currentSettings.exampleCounter + 1)
      .build()
    }
}

Java

Single<Settings> updateResult =
  dataStore.updateDataAsync(currentSettings ->
    Single.just(
      currentSettings.toBuilder()
        .setExampleCounter(currentSettings.getExampleCounter() + 1)
        .build()));

DataStore'u eşzamanlı kodda kullanma

DataStore'un temel avantajlarından biri eşzamansız API'dir, ancak her zaman çevrenizdeki kodu eşzamansız olacak şekilde değiştirebilirsiniz. Bu kullanan mevcut bir kod tabanıyla çalışıyorsanız eşzamanlı disk G/Ç'si sağlamayan bir bağımlılığınız varsa eşzamansız API.

Kotlin eş yordamları runBlocking() eşzamanlı ve eşzamansız içerikler arasındaki boşluğu kapatmaya yardımcı olacak eş yordam oluşturucu girin. DataStore'daki verileri eşzamanlı olarak okumak için runBlocking() kullanabilirsiniz. RxJava, Flowable sitesinde engelleme yöntemleri sunuyor. Aşağıdaki kod, DataStore şu verileri döndürene kadar iş parçacığı:

Kotlin

val exampleData = runBlocking { context.dataStore.data.first() }

Java

Settings settings = dataStore.data().blockingFirst();

UI iş parçacığında eşzamanlı G/Ç işlemleri yapmak ANR'ler veya kullanıcı arayüzü duraklaması. Etiketinizi eşzamansız olarak önceden yükleyerek bu sorunları azaltabilirsiniz. verileri:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        context.dataStore.data.first()
        // You should also handle IOExceptions here.
    }
}

Java

dataStore.data().first().subscribe();

Bu şekilde, DataStore verileri eşzamansız olarak okur ve önbelleğe alır. Daha sonra runBlocking() kullanılarak eşzamanlı okuma işlemleri daha hızlı olabilir veya disk G/Ç'sinden kaçınabilir tamamen devam eder.

DataStore'u çok işlemli kodda kullanma

DataStore'u farklı işlemlerde aynı verilere erişecek şekilde yapılandırabilirsiniz emin olmanızı sağlar. İçinde DataStore şunları garanti etmektedir:

  • Okuma işlemleri yalnızca diskte tutulan verileri döndürür.
  • Yazma işleminden sonra okuma tutarlılığı.
  • Yazma işlemleri serileştirilir.
  • Okumalar, yazma işlemleri tarafından hiçbir zaman engellenmez.

Hizmet ve etkinlik içeren örnek bir uygulamayı düşünün:

  1. Hizmet ayrı bir işlemde çalışıyor ve Search Ads 360'taki Veri Deposu

    <service
      android:name=".MyService"
      android:process=":my_process_id" />
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
          scope.launch {
              while(isActive) {
                  dataStore.updateData {
                      Settings(lastUpdate = System.currentTimeMillis())
                  }
                  delay(1000)
              }
          }
    }
    
  2. Uygulama, bu değişiklikleri toplayıp kullanıcı arayüzünü güncellerken

    val settings: Settings by dataStore.data.collectAsState()
    Text(
      text = "Last updated: $${settings.timestamp}",
    )
    

DataStore'u farklı işlemlerde kullanabilmek için MultiProcessDataStoreFactory kullanarak DataStore nesnesini tanımlayın.

val dataStore: DataStore<Settings> = MultiProcessDataStoreFactory.create(
   serializer = SettingsSerializer(),
   produceFile = {
       File("${context.cacheDir.path}/myapp.preferences_pb")
   }
)

serializer, DataStore'a veri türünüzü nasıl okuyacağını ve yazacağını bildirir. Aşağıdaki durumlarda kullanılacak serileştirici için varsayılan bir değer eklediğinizden emin olun: henüz dosya oluşturulmadı. Aşağıda, Arkadaş Bitkiler projesinin kotlinx.serialization:

@Serializable
data class Settings(
   val lastUpdate: Long
)

@Singleton
class SettingsSerializer @Inject constructor() : Serializer<Settings> {

   override val defaultValue = Settings(lastUpdate = 0)

   override suspend fun readFrom(input: InputStream): Timer =
       try {
           Json.decodeFromString(
               Settings.serializer(), input.readBytes().decodeToString()
           )
       } catch (serialization: SerializationException) {
           throw CorruptionException("Unable to read Settings", serialization)
       }

   override suspend fun writeTo(t: Settings, output: OutputStream) {
       output.write(
           Json.encodeToString(Settings.serializer(), t)
               .encodeToByteArray()
       )
   }
}

Hilt bağımlılığını kullanabilirsiniz ekleyin:

@Provides
@Singleton
fun provideDataStore(@ApplicationContext context: Context): DataStore<Settings> =
   MultiProcessDataStoreFactory.create(...)

Geri bildirim gönder

Aşağıdaki kaynakları kullanarak geri bildiriminizi ve düşüncelerinizi bizimle paylaşın:

Sorun izleyici
Hataları düzeltebilmemiz için sorunları bildirin.

Ek kaynaklar

Jetpack DataStore hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın:

Örnekler

Bloglar

Codelab'ler

ziyaret edin. ziyaret edin.