Uygulama Mimarisi: Veri Katmanı - DataStore - Android Geliştiricileri

Project: /architecture/_project.yaml Book: /architecture/_book.yaml keywords: datastore, architecture, api:JetpackDataStore description: Explore this app architecture guide on data layer libraries to learn about Preferences DataStore and Proto DataStore, Setup, and more. hide_page_heading: true

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

Kotlin Multiplatform ile deneme
Kotlin Multiplatform, veri katmanının diğer platformlarla paylaşılmasına olanak tanır. KMP'de DataStore'u nasıl ayarlayacağınızı ve kullanacağınızı öğrenin.

Jetpack DataStore, anahtar-değer çiftlerini veya protocol buffer ile yazılmış nesneleri depolamanıza olanak tanıyan bir veri depolama çözümüdür. DataStore, verileri eşzamansız, tutarlı ve işlemsel olarak depolamak için Kotlin coroutine'lerini ve Flow'u kullanır.

Verileri depolamak için SharedPreferences kullanıyorsanız bunun yerine DataStore'a geçiş yapmayı düşünebilirsiniz.

DataStore API

DataStore arayüzü aşağıdaki API'yi sağlar:

  1. DataStore'dan veri okumak için kullanılabilecek bir akış

    val data: Flow<T>
    
  2. DataStore'daki verileri güncelleme işlevi

    suspend updateData(transform: suspend (t) -> T)
    

DataStore Yapılandırmaları

Anahtarları kullanarak verileri depolamak ve verilere erişmek istiyorsanız önceden tanımlanmış bir şema gerektirmeyen ve tür güvenliği sağlamayan Preferences DataStore uygulamasını kullanın. SharedPreferences benzeri bir API'ye sahiptir ancak paylaşılan tercihlerle ilişkili dezavantajları yoktur.

DataStore, özel sınıfları kalıcı hale getirmenize olanak tanır. Bunu yapmak için verilerle ilgili bir şema tanımlamanız ve verileri kalıcı bir biçime dönüştürmek için Serializer sağlamanız gerekir. Protocol Buffers, JSON veya başka bir serileştirme stratejisini kullanmayı seçebilirsiniz.

Kurulum

Uygulamanızda Jetpack DataStore'u kullanmak için, kullanmak istediğiniz uygulamaya bağlı olarak Gradle dosyanıza aşağıdakileri ekleyin:

Preferences DataStore

Gradle dosyanızın bağımlılıklar bölümüne aşağıdaki satırları ekleyin:

Groovy

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

        // Alternatively - without an Android dependency.
        implementation "androidx.datastore:datastore-preferences-core:1.1.7"
    }
    

Kotlin

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

        // Alternatively - without an Android dependency.
        implementation("androidx.datastore:datastore-preferences-core:1.1.7")
    }
    

İsteğe bağlı RxJava desteği eklemek için aşağıdaki bağımlılıkları ekleyin:

Groovy

    dependencies {
        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-preferences-rxjava2:1.1.7"

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

Kotlin

    dependencies {
        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-preferences-rxjava2:1.1.7")

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

DataStore

Gradle dosyanızın bağımlılıklar bölümüne aşağıdaki satırları ekleyin:

Groovy

    dependencies {
        // Typed DataStore for custom data objects (for example, using Proto or JSON).
        implementation "androidx.datastore:datastore:1.1.7"

        // Alternatively - without an Android dependency.
        implementation "androidx.datastore:datastore-core:1.1.7"
    }
    

Kotlin

    dependencies {
        // Typed DataStore for custom data objects (for example, using Proto or JSON).
        implementation("androidx.datastore:datastore:1.1.7")

        // Alternatively - without an Android dependency.
        implementation("androidx.datastore:datastore-core:1.1.7")
    }
    

RxJava desteği için aşağıdaki isteğe bağlı bağımlılıkları ekleyin:

Groovy

    dependencies {
        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-rxjava2:1.1.7"

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

Kotlin

    dependencies {
        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-rxjava2:1.1.7")

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

İçeriği serileştirmek için Protocol Buffers veya JSON serileştirme bağımlılıklarını ekleyin.

JSON serileştirme

JSON serileştirmeyi kullanmak için Gradle dosyanıza aşağıdakileri ekleyin:

Groovy

    plugins {
        id("org.jetbrains.kotlin.plugin.serialization") version "2.2.20"
    }

    dependencies {
        implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0"
    }
    

Kotlin

    plugins {
        id("org.jetbrains.kotlin.plugin.serialization") version "2.2.20"
    }

    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
    }
    

Protobuf serileştirme

Protobuf serileştirmeyi kullanmak için Gradle dosyanıza aşağıdakileri ekleyin:

Groovy

    plugins {
        id("com.google.protobuf") version "0.9.5"
    }
    dependencies {
        implementation "com.google.protobuf:protobuf-kotlin-lite:4.32.1"

    }

    protobuf {
        protoc {
            artifact = "com.google.protobuf:protoc:4.32.1"
        }
        generateProtoTasks {
            all().forEach { task ->
                task.builtins {
                    create("java") {
                        option("lite")
                    }
                    create("kotlin")
                }
            }
        }
    }
    

Kotlin

    plugins {
        id("com.google.protobuf") version "0.9.5"
    }
    dependencies {
        implementation("com.google.protobuf:protobuf-kotlin-lite:4.32.1")
    }

    protobuf {
        protoc {
            artifact = "com.google.protobuf:protoc:4.32.1"
        }
        generateProtoTasks {
            all().forEach { task ->
                task.builtins {
                    create("java") {
                        option("lite")
                    }
                    create("kotlin")
                }
            }
        }
    }
    

DataStore'u doğru kullanma

DataStore'u doğru şekilde kullanmak için aşağıdaki kuralları her zaman göz önünde bulundurun:

  1. Aynı işlemde belirli bir dosya için hiçbir zaman birden fazla DataStore örneği oluşturmayın. Bu işlem, tüm DataStore işlevlerinin bozulmasına neden olabilir. Aynı işlemde belirli bir dosya için birden fazla DataStore etkinse DataStore, verileri okurken veya güncellerken IllegalStateException hatası verir.

  2. DataStore<T> öğesinin genel türü sabit olmalıdır. DataStore'da kullanılan bir türün değiştirilmesi, DataStore'un sağladığı tutarlılığı geçersiz kılar ve yakalanması zor, ciddi hatalara yol açabilir. Değişmezlik, net bir API ve verimli serileme sağlamaya yardımcı olan protokol arabelleklerini kullanmanızı öneririz.

  3. Aynı dosya için SingleProcessDataStore ve MultiProcessDataStore kullanımlarını karıştırmayın. DataStore öğesine birden fazla işlemden erişmeyi planlıyorsanız MultiProcessDataStore kullanmanız gerekir.

Veri Tanımı

Preferences DataStore

Verileri diske kalıcı olarak kaydetmek için kullanılacak bir anahtar tanımlayın.

val EXAMPLE_COUNTER = intPreferencesKey("example_counter")

JSON DataStore

JSON veri deposu için, kalıcı olmasını istediğiniz verilere @Serialization ek açıklaması ekleyin.

@Serializable
data class Settings(
    val exampleCounter: Int
)

Serializer<T> uygulayan bir sınıf tanımlayın. Burada T, daha önce ek açıklamayı eklediğiniz sınıfın türüdür. Henüz dosya oluşturulmamışsa kullanılacak seri hale getirici için varsayılan bir değer eklediğinizden emin olun.

object SettingsSerializer : Serializer<Settings> {

    override val defaultValue: Settings = Settings(exampleCounter = 0)

    override suspend fun readFrom(input: InputStream): Settings =
        try {
            Json.decodeFromString<Settings>(
                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(t)
                .encodeToByteArray()
        )
    }
}

Proto DataStore

Proto DataStore uygulaması, türü belirlenmiş nesneleri diske kalıcı olarak kaydetmek için DataStore ve protokol arabelleklerini kullanır.

Proto DataStore, app/src/main/proto/ dizinindeki bir proto dosyasında önceden tanımlanmış bir şema gerektirir. Bu şema, Proto DataStore'unuzda kalıcı hale getirdiğiniz nesnelerin türünü tanımlar. Proto şeması tanımlama hakkında daha fazla bilgi edinmek için protobuf dil kılavuzuna bakın.

src/main/proto klasörüne settings.proto adlı bir dosya ekleyin:

syntax = "proto3";

option java_package = "com.example.datastore.snippets.proto";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

Serializer<T> uygulayan bir sınıf tanımlayın. Burada T, proto dosyasında tanımlanan türdür. Bu serileştirici sınıfı, DataStore'un veri türünüzü nasıl okuyup yazdığını tanımlar. Henüz oluşturulmuş bir dosya yoksa kullanılacak seri hale getirici için varsayılan bir değer eklediğinizden emin olun.

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) {
        return t.writeTo(output)
    }
}

Veri deposu oluşturma

Verileri kalıcı hale getirmek için kullanılan dosyanın adını belirtmeniz gerekir.

Preferences DataStore

Preferences DataStore uygulaması, anahtar/değer çiftlerini diske kalıcı olarak kaydetmek için DataStore ve Preferences sınıflarını kullanır. DataStore<Preferences> örneği oluşturmak için preferencesDataStore tarafından oluşturulan özellik temsilcisini kullanın. Kotlin dosyanızın en üst düzeyinde bir kez çağırın. Uygulamanızın geri kalanında bu mülk aracılığıyla DataStore'a erişin. Bu sayede DataStore'unuzu tekil olarak tutmanız kolaylaşır. Alternatif olarak, RxJava kullanıyorsanız RxPreferenceDataStoreBuilder'ı kullanın. Zorunlu name parametresi, Preferences DataStore'un adıdır.

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

JSON DataStore

dataStore tarafından oluşturulan mülk temsilcisini kullanarak DataStore<T> örneği oluşturun. Burada T, serileştirilebilir veri sınıfıdır. Kotlin dosyanızın üst düzeyinde bir kez çağırın ve uygulamanızın geri kalanında bu temsilci özelliği aracılığıyla erişin. fileName parametresi, DataStore'a verileri depolamak için hangi dosyanın kullanılacağını, serializer parametresi ise 1. adımda tanımlanan serileştirici sınıfının adını bildirir.

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

Proto DataStore

dataStore tarafından oluşturulan mülk temsilcisini kullanarak DataStore<T> örneği oluşturun. Burada T, proto dosyasında tanımlanan türdür. Kotlin dosyanızın en üst düzeyinde bir kez çağırın ve uygulamanızın geri kalanında bu özellik temsilcisi aracılığıyla erişin. fileName parametresi, DataStore'a verileri depolamak için hangi dosyanın kullanılacağını, serializer parametresi ise 1. adımda tanımlanan serileştirici sınıfının adını bildirir.

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

Veri deposundan okuma

Verileri kalıcı hale getirmek için kullanılan dosyanın adını belirtmeniz gerekir.

Preferences DataStore

Preferences DataStore önceden tanımlanmış bir şema kullanmadığından, DataStore<Preferences> örneğinde depolamanız gereken her değer için bir anahtar tanımlamak üzere ilgili anahtar türü işlevini kullanmanız gerekir. Örneğin, bir int değeri için anahtar tanımlamak üzere intPreferencesKey() kullanın. Ardından, bir Flow kullanarak uygun depolanmış değeri göstermek için DataStore.data özelliğini kullanın.

fun counterFlow(): Flow<Int> = context.dataStore.data.map { preferences ->
    preferences[EXAMPLE_COUNTER] ?: 0
}

JSON DataStore

Depolanmış nesnenizden uygun özelliğin Flow değerini göstermek için DataStore.data kullanın.

fun counterFlow(): Flow<Int> = context.dataStore.data.map { settings ->
    settings.exampleCounter
}

Proto DataStore

Depolanmış nesnenizden uygun özelliğin Flow değerini göstermek için DataStore.data kullanın.

fun counterFlow(): Flow<Int> = context.dataStore.data.map { settings ->
    settings.exampleCounter
}

DataStore'a yazma

DataStore, depolanan bir nesneyi işlemsel olarak güncelleyen bir updateData() işlevi sağlar. updateData, verilerin mevcut durumunu veri türünüzün bir örneği olarak verir ve verileri atomik bir okuma-yazma-değiştirme işleminde işlemsel olarak günceller. updateData bloğundaki tüm kodlar tek bir işlem olarak değerlendirilir.

Preferences DataStore

suspend fun incrementCounter() {
    context.dataStore.updateData {
        it.toMutablePreferences().also { preferences ->
            preferences[EXAMPLE_COUNTER] = (preferences[EXAMPLE_COUNTER] ?: 0) + 1
        }
    }
}

JSON DataStore

suspend fun incrementCounter() {
    context.dataStore.updateData { settings ->
        settings.copy(exampleCounter = settings.exampleCounter + 1)
    }
}

Proto DataStore

suspend fun incrementCounter() {
    context.dataStore.updateData { settings ->
        settings.copy { exampleCounter = exampleCounter + 1 }
    }
}

Oluşturma Örneği

Bu işlevleri bir sınıfta bir araya getirebilir ve Compose uygulamasında kullanabilirsiniz.

Preferences DataStore

Artık bu işlevleri PreferencesDataStore adlı bir sınıfa yerleştirebilir ve Compose uygulamasında kullanabiliriz.

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val preferencesDataStore = remember(context) { PreferencesDataStore(context) }

// Display counter value.
val exampleCounter by preferencesDataStore.counterFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Counter $exampleCounter",
    fontSize = 25.sp
)

// Update the counter.
Button(
    onClick = {
        coroutineScope.launch { preferencesDataStore.incrementCounter() }
    }
) {
    Text("increment")
}

JSON DataStore

Artık bu işlevleri JSONDataStore adlı bir sınıfa yerleştirebilir ve Compose uygulamasında kullanabiliriz.

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val jsonDataStore = remember(context) { JsonDataStore(context) }

// Display counter value.
val exampleCounter by jsonDataStore.counterFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Counter $exampleCounter",
    fontSize = 25.sp
)

// Update the counter.
Button(onClick = { coroutineScope.launch { jsonDataStore.incrementCounter() } }) {
    Text("increment")
}

Proto DataStore

Artık bu işlevleri ProtoDataStore adlı bir sınıfa yerleştirebilir ve Compose uygulamasında kullanabiliriz.

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val protoDataStore = remember(context) { ProtoDataStore(context) }

// Display counter value.
val exampleCounter by protoDataStore.counterFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Counter $exampleCounter",
    fontSize = 25.sp
)

// Update the counter.
Button(onClick = { coroutineScope.launch { protoDataStore.incrementCounter() } }) {
    Text("increment")
}

DataStore'u eşzamanlı kodda kullanma

DataStore'un temel avantajlarından biri eşzamansız API'dir ancak çevreleyen kodunuzu eşzamansız olacak şekilde değiştirmek her zaman mümkün olmayabilir. Bu durum, eşzamanlı disk G/Ç'si kullanan mevcut bir kod tabanıyla çalışıyorsanız veya eşzamanlı olmayan bir API sağlamayan bir bağımlılığınız varsa geçerli olabilir.

Kotlin eş yordamları, senkron ve eşzamansız kod arasındaki boşluğu gidermenize yardımcı olmak için runBlocking() eş yordam oluşturucuyu sağlar. DataStore'dan verileri eşzamanlı olarak okumak için runBlocking() kullanabilirsiniz. RxJava, Flowable üzerinde engelleme yöntemleri sunar. Aşağıdaki kod, DataStore verileri döndürene kadar çağıran iş parçacığını engeller:

Kotlin

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

Java

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

Kullanıcı arayüzü iş parçacığında eş zamanlı G/Ç işlemleri gerçekleştirmek ANR'lere veya yanıt vermeyen kullanıcı arayüzüne neden olabilir. Bu sorunları, DataStore'daki verileri eşzamansız olarak önceden yükleyerek azaltabilirsiniz:

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 bellekte önbelleğe alır. İlk okuma işlemi tamamlandıysa runBlocking() kullanılarak yapılan sonraki senkron okuma işlemleri daha hızlı olabilir veya disk G/Ç işlemi tamamen önlenebilir.

Çok işlemli kodda DataStore'u kullanma

DataStore'u, tek bir işlemdekiyle aynı veri tutarlılığı özellikleriyle farklı işlemler arasında aynı verilere erişecek şekilde yapılandırabilirsiniz. DataStore özellikle şunları sağlar:

  • Okuma işlemleri yalnızca diske kalıcı olarak kaydedilmiş verileri döndürür.
  • Yazma işleminden sonra okuma tutarlılığı.
  • Yazma işlemleri sıralı olarak gerçekleştirilir.
  • Okuma işlemleri hiçbir zaman yazma işlemleri tarafından engellenmez.

Hizmetin ayrı bir işlemde çalıştığı ve DataStore'u düzenli olarak güncellediği bir hizmet ve etkinlik içeren örnek bir uygulamayı ele alalım.

Bu örnekte JSON veri deposu kullanılmaktadır ancak tercihler veya proto veri deposu da kullanabilirsiniz.

@Serializable
data class Time(
    val lastUpdateMillis: Long
)

Serileştirici, DataStore'ya veri türünüzü nasıl okuyup yazacağını söyler. Henüz oluşturulmuş bir dosya yoksa kullanılacak seri hale getirici için varsayılan bir değer eklediğinizden emin olun. Aşağıda, kotlinx.serialization kullanılarak yapılan bir örnek uygulama verilmiştir:

object TimeSerializer : Serializer<Time> {

    override val defaultValue: Time = Time(lastUpdateMillis = 0L)

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

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

DataStore öğesini farklı işlemler genelinde kullanabilmek için hem uygulama hem de hizmet kodu için MultiProcessDataStoreFactory kullanarak DataStore nesnesini oluşturmanız gerekir:

val dataStore = MultiProcessDataStoreFactory.create(
    serializer = TimeSerializer,
    produceFile = {
        File("${context.cacheDir.path}/time.pb")
    },
    corruptionHandler = null
)

AndroidManifiest.xml öğenize aşağıdakileri ekleyin:

<service
    android:name=".TimestampUpdateService"
    android:process=":my_process_id" />

Hizmet, updateLastUpdateTime() işlevini düzenli olarak çağırır. Bu işlev, updateData kullanarak veri deposuna yazar.

suspend fun updateLastUpdateTime() {
    dataStore.updateData { time ->
        time.copy(lastUpdateMillis = System.currentTimeMillis())
    }
}

Uygulama, veri akışını kullanarak hizmet tarafından yazılan değeri okur:

fun timeFlow(): Flow<Long> = dataStore.data.map { time ->
    time.lastUpdateMillis
}

Artık tüm bu işlevleri MultiProcessDataStore adlı bir sınıfta bir araya getirebilir ve bir uygulamada kullanabiliriz.

Hizmet kodu:

class TimestampUpdateService : Service() {
    val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    val multiProcessDataStore by lazy { MultiProcessDataStore(applicationContext) }


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        serviceScope.launch {
            while (true) {
                multiProcessDataStore.updateLastUpdateTime()
                delay(1000)
            }
        }
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        serviceScope.cancel()
    }
}

Uygulama kodu:

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val multiProcessDataStore = remember(context) { MultiProcessDataStore(context) }

// Display time written by other process.
val lastUpdateTime by multiProcessDataStore.timeFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Last updated: $lastUpdateTime",
    fontSize = 25.sp
)

DisposableEffect(context) {
    val serviceIntent = Intent(context, TimestampUpdateService::class.java)
    context.startService(serviceIntent)
    onDispose {
        context.stopService(serviceIntent)
    }
}

DataStore örneğinizin işlem başına benzersiz olması için Hilt bağımlılık eklemeyi kullanabilirsiniz:

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

Dosya bozulmasıyla ilgili sorunları giderme

DataStore'un diskteki kalıcı dosyasının bozulabileceği nadir durumlar vardır. DataStore, varsayılan olarak bozulmadan otomatik olarak kurtulmaz ve DataStore'dan okuma girişimleri sistemin CorruptionException oluşturmasına neden olur.

DataStore, bu tür bir senaryoda sorunsuz bir şekilde kurtulmanıza ve istisna oluşturmaktan kaçınmanıza yardımcı olabilecek bir bozulma işleyici API'si sunar. Yapılandırıldığında, bozulma işleyici, bozuk dosyayı önceden tanımlanmış bir varsayılan değer içeren yeni bir dosya ile değiştirir.

Bu işleyiciyi ayarlamak için by dataStore() içinde veya DataStoreFactory fabrika yönteminde DataStore örneğini oluştururken corruptionHandler sağlayın:

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

Geri bildirim gönder

Geri bildirimlerinizi ve fikirlerinizi aşağıdaki kaynaklar aracılığıyla bizimle paylaşabilirsiniz:

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 uygulamaları