https://medium.com/@joongwon/android-datastore로-이전하기-273329bb2569
DataStore를 소개합니다~
데이터스토어의 장점은 다음 그림 한장으로 표현할수 있습니다!
여러가지 장점이 있지만 DataStore의 가장 큰 장점은 Main UI 쓰레드가 아닌 별도의 쓰레드에서 실행을 시킬 수 있다는 점입니다.
DataStore는 Key, Value 형태로 저장하는 Preferences DataStore와 프로토콜 버퍼 형태로 저장하는 Proto DataStore 두 가지 구현을 제공합니다.
DataStore는 데이터를 읽어들이려면 Flow를 통해야 한다고 합니다. 즉, Flow를 통한다는 이야기는 코루틴에서 실행이 되어야 한다는 것을 의미하고 이는 자연스럽게 DataStore를 이용하려면 별도의 쓰레드에서 실행된다는 것을 알 수 있습니다.
Read
suspend inline fun <T : Any> DataStore<Preferences>.readValue(key: Preferences.Key<T>, defaultValue: T): T {
return data.catch { recoverOrThrow(it) }.map { it[key] }.firstOrNull() ?: defaultValue
}
suspend inline fun <T : Any> DataStore<Preferences>.readValue(key: Preferences.Key<T>): T? {
return data.catch { recoverOrThrow(it) }.map { it[key] }.firstOrNull()
}
suspend fun FlowCollector<Preferences>.recoverOrThrow(throwable: Throwable) {
if (throwable is IOException) {
emit(emptyPreferences())
} else {
throw throwable
}
}
Write
suspend inline fun <T : Any> DataStore<Preferences>.storeValue(key: Preferences.Key<T>, value: T?) {
edit { preferences ->
if (value == null) {
preferences.remove(key)
} else {
preferences[key] = value
}
}
}
Example
class ExampleDataStore(context: Context) {
private val dataStore: DataStore<Preferences> = context.dataStore
suspend fun setExampleData(exampleData: String?) {
dataStore.storeValue(EXAMPLE_DATA, exampleData)
}
suspend fun getExampleData(): String? {
return dataStore.readValue(EXAMPLE_DATA)
}
companion object {
private const val PREF_NAME = "example_pref"
private val EXAMPLE_DATA = stringPreferencesKey("exampleData")
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = PREF_NAME,
produceMigrations = { context -> listOf(SharedPreferencesMigration(context, PREF_NAME)) }
)
}
}
사실 SharedPreferences에서 read, write 비용이 보통은 크지 않기 때문에(데이터가 작고 주로 캐싱 해서 사용하죠) DataStore를 도입했을 때 얻을 수 있는 이득이 크냐?라는 관점에서 보면 효용이 크진 않을 것 같습니다. 그럼 반대로 굳이 도입하지 않을 이유가 있나?라고 반문해 보면 이것도 어려운 것 같네요.
DataStore는 적용하면서 얻는 가장 큰 이점은 SharedPreferences를 UI 스레드에서 무심코 호출하면서 생기는 찝찝함을 덜어낼 수 있다가 아닐까 싶네요.
정리해 보면 프로젝트가 도입하기에 유리한 환경이라면 별 무리 없이 적용할 수 있어서 좋을 것 같지만 굳이 DataStore를 도입하기 위해 많은 부가적인 작업들을 하면서까지 도입하기에는 충분한 메리트가 있는지 잘 모르겠습니다.
RxJava 버전도 지원을 하니 Java 환경에서도 사용이 가능할 것 같지만.. 개인적인 느낌으로는 Kotlin & Coroutine 환경에서 사용하기가 딱 좋다고 느껴지네요.