DataStore Android Jetpack'in bir parçasıdır.
Jetpack DataStore, anahtar/değer çiftlerini veya yazılan nesneleri protokol arabellekleriyle 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 eş yordamlarını ve Flow uygulamasını kullanır.
Şu anda veri depolamak için SharedPreferences
kullanıyorsanız bunun yerine DataStore'a geçiş yapabilirsiniz.
Tercihler DataStore ve Proto DataStore
DataStore iki farklı uygulama sunar: Tercihler DataStore ve Proto DataStore.
- Preferences DataStore, verileri depolar ve anahtarları kullanarak erişir. Bu uygulama, önceden tanımlanmış bir şema gerektirmez ve tür güvenliği sağlamaz.
- Proto DataStore, verileri özel bir veri türünün örnekleri olarak depolar. Bu uygulama, protokol arabellekleri kullanarak bir şema tanımlamanızı gerektirir ancak tür güvenliği sağlar.
DataStore'u doğru kullanma
DataStore'u doğru şekilde kullanmak için aşağıdaki kuralları her zaman göz önünde bulundurun:
Aynı işlemde belirli bir dosya için hiçbir zaman birden fazla
DataStore
örneği oluşturmayın. Aksi takdirde tüm DataStore işlevleri bozulabilir. Aynı işlemde belirli bir dosya için etkin olan birden fazla DataStore varsa DataStore verileri okurken veya güncellerkenIllegalStateException
değerini atar.DataStore'un genel türü
sabit olmalıdır. DataStore'da kullanılan bir türün değiştirilmesi, DataStore'un sağladığı tüm garantileri geçersiz kılar ve potansiyel olarak ciddi, bulunması zor hatalar oluşturur. Değişmezlik garantisi, basit bir API ve verimli serileştirme sağlayan protokol arabellekleri kullanmanız önemle tavsiye edilir.Aynı dosya için hiçbir zaman
SingleProcessDataStore
veMultiProcessDataStore
kullanımlarını karıştırmayın.DataStore
öğesine birden fazla işlemden erişmeyi planlıyorsanız her zamanMultiProcessDataStore
kullanın.
Kurulum
Uygulamanızda Jetpack DataStore kullanmak için, kullanmak istediğiniz uygulamaya bağlı olarak aşağıdakileri Gradle dosyanıza ekleyin:
Tercihler DataStore
Modern
// Preferences DataStore (SharedPreferences like APIs) dependencies { implementation "androidx.datastore:datastore-preferences:1.0.0" // optional - RxJava2 support implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0" // optional - RxJava3 support implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0" } // Alternatively - use the following artifact without an Android dependency. dependencies { implementation "androidx.datastore:datastore-preferences-core:1.0.0" }
Kotlin
// Preferences DataStore (SharedPreferences like APIs) dependencies { implementation("androidx.datastore:datastore-preferences:1.0.0") // optional - RxJava2 support implementation("androidx.datastore:datastore-preferences-rxjava2:1.0.0") // optional - RxJava3 support implementation("androidx.datastore:datastore-preferences-rxjava3:1.0.0") } // Alternatively - use the following artifact without an Android dependency. dependencies { implementation("androidx.datastore:datastore-preferences-core:1.0.0") }
Proto Veri Deposu
Modern
// Typed DataStore (Typed API surface, such as Proto) dependencies { implementation "androidx.datastore:datastore:1.0.0" // optional - RxJava2 support implementation "androidx.datastore:datastore-rxjava2:1.0.0" // optional - RxJava3 support implementation "androidx.datastore:datastore-rxjava3:1.0.0" } // Alternatively - use the following artifact without an Android dependency. dependencies { implementation "androidx.datastore:datastore-core:1.0.0" }
Kotlin
// Typed DataStore (Typed API surface, such as Proto) dependencies { implementation("androidx.datastore:datastore:1.0.0") // optional - RxJava2 support implementation("androidx.datastore:datastore-rxjava2:1.0.0") // optional - RxJava3 support implementation("androidx.datastore:datastore-rxjava3:1.0.0") } // Alternatively - use the following artifact without an Android dependency. dependencies { implementation("androidx.datastore:datastore-core:1.0.0") }
Anahtar/değer çiftlerini Tercihler DataStore ile depolayın
Tercihler DataStore uygulaması, basit anahtar/değer çiftlerini diskte korumak için DataStore
ve Preferences
sınıflarını kullanır.
Tercihler Veri Deposu oluşturun
Datastore<Preferences>
örneği oluşturmak için preferencesDataStore
tarafından oluşturulan mülk yetkisini kullanın. Kodu kotlin dosyanızın en üst düzeyinde bir kez çağırın ve uygulamanızın geri kalanında bu mülk üzerinden dosyaya erişin. Bu, DataStore
öğenizi tekilleştirmenizi kolaylaştırır. RxJava kullanıyorsanız alternatif olarak RxPreferenceDataStoreBuilder
'i de kullanabilirsiniz. Zorunlu name
parametresi, Tercihler DataStore'un adıdır.
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 Veri Deposu'ndan okuma
Tercihler 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 karşılık gelen anahtar türü işlevini kullanmanız gerekir. Örneğin, bir int değeri için anahtar tanımlamak üzere intPreferencesKey()
değerini kullanın.
Ardından, Flow
kullanarak uygun depolanmış değeri ortaya çıkarmak için DataStore.data
özelliğini kullanın.
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 yazma
Tercihler DataStore, verileri bir DataStore
içinde işlem olarak güncelleyen bir edit()
işlevi sağlar. İşlevin transform
parametresi, değerleri gerektiği gibi güncelleyebileceğiniz bir kod bloğunu kabul eder. Dönüşüm bloğundaki tüm kod tek bir işlem olarak kabul edilir.
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ı, yazılan nesneleri diske tutmak için DataStore ve protokol arabelleklerini kullanır.
Şema tanımlama
Proto DataStore için app/src/main/proto/
dizinindeki bir proto dosyasında önceden tanımlanmış bir şema gerekir. Bu şema, Proto DataStore'da tuttuğunuz nesnelerin türünü tanımlar. Proto şeması tanımlama hakkında daha fazla bilgi edinmek için protobuf dil kılavuzuna bakın.
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 nesneleri depolamak için Proto DataStore iki adımdan oluşur:
Serializer<T>
yöntemini uygulayan bir sınıf tanımlayın. BuradaT
, proto dosyasında tanımlanan türdür. Bu serileştirici sınıfı, DataStore'a veri türünüzü nasıl okuyup yazacağını anlatır. Henüz dosya oluşturulmadıysa kullanılacak serileştirici için varsayılan bir değer eklediğinizden emin olun.DataStore<T>
örneği oluşturmak içindataStore
tarafından oluşturulan mülk yetki öğesini kullanın. BuradaT
, proto dosyasında tanımlanan türdür. Bunu kotlin dosyanızın en üst düzeyinde bir kez çağırın ve uygulamanızın geri kalanında bu özellik yetkisi aracılığıyla dosyaya erişin.filename
parametresi, verileri depolamak için hangi dosyanın kullanılacağını DataStore'a,serializer
parametresi ise DataStore'a 1. adımda tanımlanan serileştirici sınıfının adını bildirir.
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
Depolanmış nesnenizden uygun özelliğin bir Flow
değerini 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 yazma
Proto DataStore, depolanan bir nesneyi işlemsel olarak güncelleyen bir updateData()
işlevi sunar. updateData()
, veri türünüzün örneği olarak verilerin mevcut durumunu verir ve atomik okuma-yazma-değiştirme işleminde verileri işlemsel olarak günceller.
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 başlıca avantajlarından biri eşzamansız API'dir ancak çevresindeki kodu eşzamansız olacak şekilde değiştirmek her zaman uygun olmayabilir. Eşzamanlı disk G/Ç kullanan mevcut bir kod tabanıyla çalışıyorsanız veya eşzamansız API sağlamayan bir bağımlılığınız varsa bu durum yaşanabilir.
Kotlin eş yordamları, eşzamanlı ve eşzamansız kod arasındaki boşluğu doldurmaya yardımcı olmak için runBlocking()
eş yordam oluşturucuyu sunar. DataStore'dan verileri eşzamanlı olarak okumak için runBlocking()
kullanabilirsiniz.
RxJava, Flowable
üzerinde engelleme yöntemleri sunar. Aşağıdaki kod, DataStore veri döndürene kadar çağrı yapan 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 kullanıcı arayüzü duraklamalarına neden olabilir. Verileri DataStore'dan eşzamansız olarak önceden yükleyerek bu sorunları 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. runBlocking()
kullanılarak yapılan daha sonra eşzamanlı okumalar daha hızlı olabilir veya ilk okuma tamamlanırsa disk G/Ç işlemini tamamen önleyebilir.
DataStore'u çok işlemli kodda kullanma
DataStore'u, tek bir işlemdekiyle aynı veri tutarlılığı garantileri ile aynı verilere farklı işlemlerde erişecek şekilde yapılandırabilirsiniz. DataStore özellikle şunları garanti eder:
- Okumalar yalnızca diskte saklanan verileri döndürür.
- Yazma sonrası okuma tutarlılığı.
- Yazma işlemleri serileştirilir.
- Okumalar hiçbir zaman yazma işlemleri tarafından engellenmez.
Hizmeti ve etkinliği olan örnek bir uygulama düşünün:
Hizmet ayrı bir işlemde çalışır ve DataStore düzenli olarak güncellenir.
<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) } } }
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 DataStore nesnesini MultiProcessDataStoreFactory
.
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 okuyup yazacağını bildirir.
Henüz dosya oluşturulmadıysa kullanılacak serileştirici için varsayılan bir değer eklediğinizden emin olun. Aşağıda kotlinx.serialization kullanan bir örnek uygulama verilmiştir:
@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()
)
}
}
DataStore örneğinizin işlem başına benzersiz olduğundan emin olmak için Hilt bağımlılık ekleme işlemini kullanabilirsiniz:
@Provides
@Singleton
fun provideDataStore(@ApplicationContext context: Context): DataStore<Settings> =
MultiProcessDataStoreFactory.create(...)
Geri bildirim gönderme
Aşağıdaki kaynakları kullanarak geri bildirimlerinizi ve fikirlerinizi 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 kaynakları inceleyin:
Sana Özel
Bloglar
Codelab uygulamaları
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken gösterilir
- Sayfalı verileri yükleme ve görüntüleme
- LiveData'ya genel bakış
- Düzenler ve bağlama ifadeleri