
Jetpack Datastore는 프로토콜 버퍼를 사용하여 키-값 쌍 또는 유형이 지정된 객체를 저장할 수 있는 데이터 저장소 솔루션입니다. Datastore는 Kotlin 코루틴 및 Flow를 사용하여 비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장합니다.
현재 SharedPreferences를 사용하여 데이터를 저장하고 있다면 대신 Datastore로 이전하는 것이 좋습니다.
비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장하여 SharedPreferences의 일부 단점을 극복합니다. - Android Developer

= SharePreferences를 대체하는 Data Storage Solution

요약해서, SharedPreferences 대신 Datastore를 사용해야하는 이유는?
DataStore은 Dispatcher.IO를 내부적으로 사용하기 때문에 UI 스레드(메인 스레드)에서 호출하는 것이 안전하다. (main-safety)
Runtime Exception으로부터 안정적
변경 사항을 저장하기 위해 apply() 또는 commit() 기능을 사용할 필요가 없다.
데이터 업데이트를 트랜잭션 방식으로 처리한다.
Kotlin Flow 및 LiveData와도 communication 가능한 라이브러리로 보다 정교한 옵션으로 사용 가능하다. (ex. 데이터의 현재 상태를 나타내는 Flow를 노출함)
키를 사용하여 데이터를 저장하고 데이터에 접근합니다. 이 구현은 type 안정성을 제공하지 않으며 사전 정의된 스키마가 필요하지 않습니다.
맞춤 데이터 유형의 인스턴스로 데이터를 저장합니다. 이 구현은 type 안정성을 제공하며 프로토콜 버퍼를 사용하며 스키마를 정의해야 합니다.
// 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"
}
물론 core artifact를 사용한다면 Proguard 규칙을
proguard-rules.pro파일에 직접 추가하여 필드가 삭제되지 않도록 해야 합니다.
-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite {
<fields>;
}
// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
최상위 수준에서 인스턴트 한 번 호출한 후 싱글톤으로 DataStore 유지. RxJava를 사용하는 경우 RxPreferenceDataStoreBuilder를 사용합니다.
DI를 사용한다면 아래와 같이 lifecycle은 @Singleton으로 선언 후 사용할 수도 있습니다.
@Singleton
@Provides
fun provideDataStore(@ApplicationContext app: Context
) : DataStore = app.dataStore
이 때 name은 필수 파라미터 입니다.
DataStore<Preferences> 인스턴스에 저장해야 하는 각 값의 키를 정의하려면 선언하고자 하는 키 유형 함수를 맞게 사용해야 합니다.
예를 들어 INT 값의 키를 정의하려면 inPreferencesKey()를 사용하면 됩니다. 그런 다음 DataStore.data 속성을 사용하여 Flow를 사용한 적절한 저장 값을 노출합니다.
stringPreferencesKey(key)
booleanPreferencesKey(key)
doublePreferencesKey(key)
floatPreferencesKey(key)
longPreferencesKey(key)
readExample.kt
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
val exampleCounterFlow: Flow<Int> = context.dataStore.data
.map { preferences ->
// No type safety.
preferences[EXAMPLE_COUNTER] ?: 0
}
Preferences DataStore는 DataStore의 데이터를 transactionally으로 업데이트하는 edit() 함수를 제공합니다. 함수의 transform 매개변수는 필요에 따라 값을 업데이트할 수 있는 코드 block을 허용합니다. transform block 안에 있는 모든 코드는 단일 트랜잭션으로 취급됩니다.
suspend fun incrementCounter() {
context.dataStore.edit { settings ->
val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
settings[EXAMPLE_COUNTER] = currentCounterValue + 1
}
}
suspend fun save(key: String,value: String){
val dataStoreKey = stringPreferencesKey(key)
this.dataStore.edit { settings ->
settings[dataStoreKey] = value
}
}
suspend fun read(key: String): String {
val dataStoreKey = stringPreferencesKey(key)
val preferences = dataStore.data.first()
return preferences[dataStoreKey] ?: "default"
}
CoroutineScope(Dispatchers.Main).launch{
var isTheme = read("THEME")
Log.d("TAG", "onCreate: themevalue $isTheme")
save("THEME","2")
isTheme = read("THEME")
Log.d("TAG", "onCreate: themevalue after save $isTheme")
}
D/TAG: onCreate: themevalue default
D/TAG: onCreate: themevalue after save 2
https://developer.android.com/topic/libraries/architecture/datastore#prefs-vs-proto
https://proandroiddev.com/lets-explore-jetpack-datastore-in-android-621f3564b57
https://we-launch.com/3-main-steps-to-learn-jetpack-datastore-preference/
https://medium.com/scalereal/hello-datastore-bye-sharedpreferences-android-f46c610b81d5
https://developer.android.google.cn/codelabs/android-preferences-datastore?hl=ko#0