Preference DataStore : JetPack Lib - Part 1

woga·2021년 10월 10일
1

Android 공부

목록 보기
11/49
post-thumbnail

What is DataStore?

Jetpack Datastore는 프로토콜 버퍼를 사용하여 키-값 쌍 또는 유형이 지정된 객체를 저장할 수 있는 데이터 저장소 솔루션입니다. Datastore는 Kotlin 코루틴 및 Flow를 사용하여 비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장합니다.

현재 SharedPreferences를 사용하여 데이터를 저장하고 있다면 대신 Datastore로 이전하는 것이 좋습니다.

비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장하여 SharedPreferences의 일부 단점을 극복합니다. - Android Developer

  • 최근 라이브러리 버전 1.0.0

  • Jetpack DataStore은 데이터 저장 솔루션이다.
  • 키-값이 쌍을 이뤄 (또는 objects 타입을) 프로토콜 버퍼와 함께 저장할 수 있다.
  • DataStore은 Kotlin 코루틴과 Flow를 사용하여 데이터를 비동기식, 일관적, 트랜잭션 방식(transactionally)으로 저장한다.

= SharePreferences를 대체하는 Data Storage Solution

JetPack DataStore vs SharedPreferences

요약해서, SharedPreferences 대신 Datastore를 사용해야하는 이유는?

  • DataStore은 Dispatcher.IO를 내부적으로 사용하기 때문에 UI 스레드(메인 스레드)에서 호출하는 것이 안전하다. (main-safety)

  • Runtime Exception으로부터 안정적

  • 변경 사항을 저장하기 위해 apply() 또는 commit() 기능을 사용할 필요가 없다.

  • 데이터 업데이트를 트랜잭션 방식으로 처리한다.

  • Kotlin Flow 및 LiveData와도 communication 가능한 라이브러리로 보다 정교한 옵션으로 사용 가능하다. (ex. 데이터의 현재 상태를 나타내는 Flow를 노출함)

또한, DataStore은 두 가지 구현을 제공합니다.

  1. Preferences Datastore

키를 사용하여 데이터를 저장하고 데이터에 접근합니다. 이 구현은 type 안정성을 제공하지 않으며 사전 정의된 스키마가 필요하지 않습니다.

  1. Proto Datastore

맞춤 데이터 유형의 인스턴스로 데이터를 저장합니다. 이 구현은 type 안정성을 제공하며 프로토콜 버퍼를 사용하며 스키마를 정의해야 합니다.

Preferences Datastore

  • 그래들 설정
    // 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>;
}


### create preferences datastore `DataStore` 클래스와 `Preferences` 클래스를 사용하여 간단한 키-값을 유지합니다.
// 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은 필수 파라미터 입니다.



read preferences Datasotre

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
}


write Preferences Datasotre

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
  }
}

Easier Example

save fun

suspend fun save(key: String,value: String){
    val dataStoreKey = stringPreferencesKey(key)
    this.dataStore.edit { settings ->
        settings[dataStoreKey] = value
    }
}

read fun

suspend fun read(key: String): String {
    val dataStoreKey = stringPreferencesKey(key)
    val preferences = dataStore.data.first()
    return preferences[dataStoreKey]  ?: "default"
}

call funs

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")
}

logcat

D/TAG: onCreate: themevalue default
D/TAG: onCreate: themevalue after save 2

Reference

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/supercharges-mobile-product-guide/new-way-of-storing-data-in-android-jetpack-datastore-a1073d09393d

https://medium.com/scalereal/hello-datastore-bye-sharedpreferences-android-f46c610b81d5

https://developer.android.google.cn/codelabs/android-preferences-datastore?hl=ko#0

profile
와니와니와니와니 당근당근

0개의 댓글