프로그래밍/Android

[Android] Preferences DataStore 사용법과 개념

Lou Park 2020. 10. 9. 15:36

원문 

https://medium.com/@shalutd007/welcome-datastore-good-bye-sharedpreferences-4bf68e70efdb

https://developer.android.com/topic/libraries/architecture/datastore

원문을 읽으면서 제가 공부하려고 번/의역한 것입니다.


Jetpack DataStore

DataStore는 Key-Value쌍 또는 Protocol buffers를 이용한 Typed Object형태로 데이터를 저장할 수 있게 해주는 솔루션이다.

DataStre는 Kotlin의 Coroutine과 Flow를 사용해서 데이터를 비동기적이고, 일관적으로 저장할 수 있다. 또한 SharedPreference로 부터의 migration도 지원하고있어서 현재 작업중인 프로젝트에서 대체하기도 편하다.


DataStore의 종류

DataStore는 2가지 종류가 있다. 


1. Preferences DataStore

SharedPreferences 처럼 Key를 이용해 데이터를 저장하고, 접근한다.

스키마를 정의 하거나, 타입을 보장받을 수 없다.


2. Proto DataStore

사용자가 정의한 형식의 데이터를 저장할 수 있다.

Protocol buffers를 통해서 스키마를 정의해야한다. Protocol buffers는 데이터의 타입을 보장해준다.

SharedPrefences가 데이터를 저장하는 방법인 XML 등의 형식보다 훨씬더 빠르고, 단순하다.



Prefences DataStore 사용하는 방법

1. 필요한 디펜던시를 app module에 추가한다.

1
2
3
4
dependencies {
    // Preferences DataStore
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"
}
cs



2. Preferences DataStore를 생성한다.

createDataStore의 인자에는 이름을 쓴다.

1
2
3
class PrefSettingsManager(val context: Context) {
    private val dataStore = context.createDataStore("datastore_name")
}
cs


3. Preferences DataStore에서 읽기

Preferences DataStore는 사전에 정의된 스키마를 사용하지 않기 때문에, preferencesKey() 메소드를 사용하여 저장할 값 각각에 대해 Key를 정의해야한다.

그리고 나서 DataStore.data 속성을 통해 Flow로 적절한 값을 가져올 수 있다.


1
2
3
4
5
val EXAMPLE_COUNTER = preferencesKey<Int>("example_counter"// Key은 example_counter이고, Int형 값임
val exampleCounterFlow = Flow<Int> = dataStore.data
    .map { prefs ->
        prefs[EXAMPLE_COUNTER] ?: 0
    }
cs


4. Preferences DataStore에 쓰기

Preferences DataStore는 데이터를 트랜잭션 방식으로 업데이트하는 edit()이라고하는 메소드를 제공한다. edit 블록 안에있는 코드는 모두 하나의 트랜잭션으로 간주된다.


1
2
3
4
5
6
suspend fun incrementCounter() {
    dataStore.edit { settings ->
        val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
        settings[EXAMPLE_COUNTER] = currentCounterValue + 1
    }
}
cs


5. Activity에서 값 읽고 쓰기

아래 코드는 액티비티에서 DataStore에 저장된 값을 읽고, 쓰는 간단한 예제다.


액티비티에 "Black" 이랑 "White"라고 적혀있는 버튼이있고, 유저가 버튼을 누르면 

Black -> android.R.color.black

White -> androidR.color.white  라는 값이 DataStore에 저장된다.


그리고 Flow로현재 DataStore에 있는 값을 읽어 배경색으로 지정한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class PrefDataStoreActivity : AppCompatActivity() {
    private lateinit var prefSettingsManager: PrefSettingsManager
    private lateinit var outerView: View
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_datastore)
 
        title = getString(R.string.preferences_datastore)
        outerView = findViewById(R.id.outerView)
 
        prefSettingsManager = PrefSettingsMaanger(this)
        readSettings()
 
        btn_white.setOnClickListener {
            updateSettings(android.R.color.white)
        }
        btn_black.setOnClickListener {
            updateSettings(android.R.color.black)
        }
    }
 
    // 쓰기
    fun updateSettings(color: Int) {
        GlobalScope.launch {
            prefSettingsManager.updateColor(color)
        }
    }
 
    // 읽기
    fun readSettings() {
        GlobalScope.launch {
            prefSettingsManager.userPreferencesFlow.collect {
                // 배경색 지정
                outerView.setBackgroundColor(
                    ContextCompat.getColor(this@PrefdataStoreActivity, it.color)
                )
            }
        }
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PrefSettingsManager(val context: Context) {
    private val dataStore = context.createDataStore("settings_pref")
 
    val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
        .catch { exception ->
            if (exception is IOException) {
                emit(emptyPreferences())
            } else {
                throw exception
            }
        }.map { preferences ->
            val color = preferences[BG_COLOR]?: android.R.color.white
            UserPreferences(color)
        }
 
    suspend fun updateColor(color: Int) {
        dataStore.edit { preferences ->
            preferences[BG_COLOR] = color
        }
    }
}
cs





- 글이 길어져서 Proto DataStore는 따로 다뤄보도록 하겠습니다.