DataStore, Android uygulamalarında küçük miktarlarda veri depolamak için tasarlanmış Jetpack kütüphanesinin bir bileşenidir. SharedPreferences'ın aksine, asenkron olarak çalışır ve eşzamanlı erişim altında veri tutarlılığını garanti eder. Google, 2024'e göre, DataStore Kotlin Coroutines ve Flow kullanır, bu da onu ana iş parçacığı için güvenli ve reaktif mimariler için uygun hale getirir.
Önemli Noktalar
DataStore, Android'de yerel veri depolama için Google'ın çözümüdür ve 2020'de SharedPreferences'a alternatif olarak sunulmuştur. İki modu destekler: Preferences DataStore (basit anahtar-değer çiftleri) ve Proto DataStore (Protocol Buffers tabanlı tür belirtilmiş şema).
Ana avantaj tam asenkronizasyondur: tüm okuma işlemleri Kotlin Coroutines'den Flow döndürür ve yazmalar coroutine bağlamında gerçekleştirilir. Bu, büyük miktarlarda veriyle çalışırken SharedPreferences'ın tipik bir sorunu olan ana iş parçacığı blokajını ortadan kaldırır.
DataStore, işlemlerin atomikliğini garanti eder: işlemsel modeli sayesinde eşzamanlı yazmalar veri kaybına yol açmaz. İki bileşen aynı anda aynı değeri değiştirirse, DataStore compare-and-swap mekanizması aracılığıyla çakışmayı doğru şekilde işler.
Google I/O 2023'e göre DataStore, yeni Android projelerinin %40'ında kullanılıyor ve Google, ayar depolama kararlılığı gereken tüm uygulamalarda SharedPreferences'tan geçiş yapılmasını öneriyor.
DataStore'un merkezinde SingleProcessDataStore bulunur — tek bir süreç içinde çalışan bir uygulama. Dosya düzeyinde kilitleme ile dosya tabanlı depolama kullanır: veri yazarken dosya kilitlenir ve eşzamanlı erişim altında bozulmayı önler.
DataStore, seri durumdan çıkarma hatalarını otomatik olarak işler: dosya bozuksa varsayılan bir değer döndürür ve dosyayı üzerine yazar. Bu davranış, DataStore oluşturulurken ayarlanabilen corruptionHandler aracılığıyla yapılandırılabilir.
SharedPreferences üç temel sorundan muzdariptir: ana iş parçacığında senkron disk okuma, eşzamanlı yazmalar için atomiklik garantilerinin olmaması ve değişiklikleri reaktif olarak izleyememe. DataStore üçünü de çözer: gözlem için Flow, atomiklik için dosya kilitleme ve iş parçacığı güvenliği için asenkron API.
DataStore verileri cihazın dahili deposundaki dosyalarda saklar. Preferences DataStore, SharedPreferences'a benzer bir dosya formatı kullanır ancak bütünlük kontrolü için ek meta verilerle birlikte. Proto DataStore, ikili Protocol Buffers formatını kullanır, bu da dosya boyutunu küçültür ve serileştirmeyi hızlandırır.
Veri okurken DataStore tüm dosyayı bir kez belleğe yükler, ardından aboneler Flow aracılığıyla güncel durumu alır. Değişiklikler tüm aktif abonelere otomatik olarak iletilir — SharedPreferences'taki gibi manuel dinleyici kaydı gerekmez.
Preferences DataStore, Map tabanlı yerleşik bir serileştirme mekanizması kullanır. Her giriş, bir dize ve bir ilkel türün (Int, Boolean, Float, Long, String, Set) çiftidir. Veriler SharedPreferences'a benzer bir XML dosyasında saklanır, ancak dosya kilitleme yoluyla atomik yazma ile.
Preferences DataStore oluşturma örneği: Context üzerindeki preferencesDataStore uzantısı, dosya adıyla bir singleton oluşturur. Tekrarlanan çağrılarda aynı örnek döndürülür — bu, dosya tekrarını ve farklı depolama örnekleri arasındaki karışıklığı ortadan kaldırır.
Proto DataStore, .proto dosyası aracılığıyla bir veri şeması tanımlamayı ve protobuf eklentisi ile derlemeyi gerektirir. Oluşturulan Java sınıfı, tüm alanlar için tek giriş noktası olarak kullanılır — bu, SharedPreferences'da yaygın olan anahtar yazım hatalarını ortadan kaldırır.
Proto DataStore şeması bir kez tanımlanır ve eski verileri kaybetmeden yeni alanlar eklemeyi destekler. Uygulamanın yeni bir sürümü varsayılan değere sahip bir alan eklerse, eski dosya doğru şekilde seri durumdan çıkarılacaktır — geriye dönük uyumluluk protokole yerleşiktir.
Preferences DataStore ve Proto DataStore arasındaki seçim, veri karmaşıklığına ve tür belirleme gereksinimlerine bağlıdır. Her iki seçenek de asenkron ve işlemseldir, ancak tür güvenliği ve serileştirme performansında farklılık gösterir.
| Özellik | Preferences DataStore | Proto DataStore |
|---|---|---|
| Tür belirleme | Zayıf (anahtar-değer) | Güçlü (oluşturulan sınıf) |
| Serileştirme | XML (yerleşik) | Protocol Buffers (protobuf) |
| Dosya boyutu | Büyük (okunabilir XML) | Küçük (ikili) |
| Karmaşıklık | Düşük (.proto gerekmez) | Orta (.proto gerekli) |
| Şema geçişi | Şema yok | Otomatik (proto) |
| Uyumluluk | SharedPreferences (geçiş yoluyla) | Yalnızca Proto DataStore |
Preferences DataStore basit ayarlar için uygundur: özellik bayrakları, yetkilendirme token dizesi, uygulama başlatma sayısı. Veri azsa (10–15 anahtara kadar) ve katı bir şema gerektirmiyorsa, Preferences DataStore protobuf eklentisini bağlamadan minimum giriş eşiği sağlar.
Proto DataStore, veri yapısı karmaşık olduğunda veya uygulama sürümleri arasında değişebildiğinde haklı çıkar. Örneğin, kullanıcı profili ayarları veya 20+ alanlı A/B test yapılandırması. Protobuf güçlü tür belirleme ve otomatik geçişler sağlayarak anahtar uyuşmazlıklarından kaynaklanan çalışma zamanı hatalarını ortadan kaldırır.
Google, SharedPreferencesMigration sınıfı aracılığıyla yerleşik bir geçiş mekanizması sağlar. Geçiş, uygulama güncellemesinden sonra ilk başlatmada bir kez gerçekleştirilir: DataStore, SharedPreferences'tan verileri okur, kendi formatında yazar ve geçişi tamamlanmış olarak işaretler.
Geçiş, özel dönüşümleri destekler: SharedPreferences'taki anahtarlar istenen DataStore anahtarlarıyla eşleşmiyorsa, SharedPreferencesMigration aracılığıyla bir dönüşüm işlevi belirtebilirsiniz. Bu, geçiş sırasında anahtarları yeniden adlandırmaya ve veri türlerini değiştirmeye olanak tanır.
İlk olarak, DataStore'u build.gradle'a ekleyin ve geçişle birlikte bir DataStore örneği oluşturun: SharedPreferencesMigration, SharedPreferences dosya adını ve aktarılacak anahtarlar kümesini kabul eder. İkinci olarak, SharedPreferences aracılığıyla çalışan tüm kodu kaldırın ve DataStore çağrılarıyla değiştirin. Üçüncü olarak, geçişi test edin: ilk başlatmada veriler DataStore'da görünmeli ve eski SharedPreferences dosyası artık kullanılmamalıdır.
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(
SharedPreferencesMigration(context, "old_prefs")
)
}
)
DataStore mevcut bir projeye kolayca entegre olur. Aşağıda Preferences DataStore ve Proto DataStore için pratik örnekler verilmiştir — her ikisi de veri okuma, yazma ve reaktif gözlemi göstermektedir.
Bu örnekte, Preferences DataStore üç ayarı saklar: karanlık tema, kullanıcı adı ve başlatma sayısı. Okuma, Flow döndüren .data uzantısı aracılığıyla yapılır. Yazma, değişikliklerin atomikliğini garanti eden .edit askıya alma işlevi aracılığıyla yapılır.
val Context.settingsDataStore by preferencesDataStore(name = "settings")
val isDarkMode: Flow<Boolean> = settingsDataStore.data
.map { preferences ->
preferences[booleanPreferencesKey("dark_mode")] ?: false
}
suspend fun toggleDarkMode() {
settingsDataStore.edit { prefs ->
val current = prefs[booleanPreferencesKey("dark_mode")] ?: false
prefs[booleanPreferencesKey("dark_mode")] = !current
}
}
Proto DataStore bir .proto dosyası tanımlamayı gerektirir. Derlemeden sonra, okuma ve yazma için kullanılan bir UserSettings sınıfı oluşturulur. Şemanın sürüm geçişleri aynı .proto dosyasında tanımlanır ve otomatik olarak uygulanır.
// user_preferences.proto
syntax = "proto3";
message UserPreferences {
string display_name = 1;
int32 notification_count = 2;
bool notifications_enabled = 3;
}
// DataStore'dan okuma
val userPreferencesFlow: Flow<UserPreferences> =
protoDataStore.data
// Yeni değerler yazma
suspend fun updateDisplayName(name: String) {
protoDataStore.updateData { prefs ->
prefs.toBuilder()
.setDisplayName(name)
.build()
}
}
DataStore ViewModel aracılığıyla MVVM mimarisiyle entegre olur. DataStore'dan Flow, .stateIn aracılığıyla toplanır ve UI'da kullanılır. Her veri değişikliğinde UI otomatik olarak güncellenir — manuel güncelleme veya LiveData gerekmez.
class SettingsViewModel(
private val dataStore: DataStore<Preferences>
) : ViewModel() {
val uiState: StateFlow<SettingsUiState> =
dataStore.data
.map { prefs ->
SettingsUiState(
isDarkMode = prefs[booleanPreferencesKey("dark_mode")] ?: false,
counter = prefs[intPreferencesKey("launch_count")] ?: 0
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = SettingsUiState()
)
}
Sıkça Sorulan Sorular
DataStore asenkron çalışır (UI iş parçacığını bloke etmez), işlemler aracılığıyla eşzamanlı erişimi destekler ve Flow aracılığıyla değişikliklere reaktif aboneliğe izin verir. SharedPreferences, büyük veri hacimlerinde ANR riski olan ve yerleşik reaktivite desteği olmayan senkron bir API'dir.
DataStore Kotlin'de yazılmıştır ve Kotlin Coroutines gerektirir. Java'dan kullanmak mümkün ancak zahmetlidir: CompletableFuture ile sarmalayıcılar oluşturmanız veya coroutine'leri manuel olarak yönetmeniz gerekir. Java projeleri için Google, SharedPreferences'ı korumayı veya modüle Kotlin eklemeyi önerir.
DataStore okurken tüm dosyayı belleğe yükler, bu nedenle listeleri veya büyük nesneleri depolamak için uygun değildir. Bu tür senaryolar için Room veya SQLite kullanın. DataStore, ayarlar ve yüzlerce kilobayta kadar küçük yapılandırılmış veriler için optimize edilmiştir.
DataStore oluştururken bir corruptionHandler iletebilirsiniz — dosya bozulduğunda çağrılan bir işlev. Varsayılan olarak DataStore bir CorruptionException fırlatır. corruptionHandler'da boş veri döndürebilirsiniz, ardından DataStore dosyayı doğru bir durumla üzerine yazacaktır.
Evet, Proto DataStore bir .proto dosyasında şema tanımlamayı ve protobuf-gradle-plugin'u bağlamayı gerektirir. Proje küçükse ve veriler basitse, Preferences DataStore kullanmak daha kolaydır — ek derleme yapılandırması gerektirmez.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun