기존의 Sharedpreference 를 DataStore Migration 하면서 나왔던 문제들과 적용하는 법
Jetpack DataStore를 환영합니다. SharedPreferences를 대체하기 위한 새롭고 향상된 데이터 저장소 솔루션입니다. Kotlin 코루틴과 Flow를 기반으로 구축된 DataStore는 유형이 지정된 객체 ( 프로토콜 버퍼 로 지원 )를 저장할 수 있는 Proto DataStore 와 키-값 쌍을 저장하는 Preferences DataStore 라는 두 가지 구현을 제공합니다 . 데이터는 비동기식, 일관적, 트랜잭션 방식으로 저장되어 SharedPreferences의 단점을 대부분 극복합니다.
SharedPreferences 문제점 : UI 스레드에서 호출해도 안전해 보일 수 있는 동기 API, 오류 신호를 위한 메커니즘 없음, 트랜잭션 API 부족 등 여러 가지 단점이 있습니다.
DataStore 장점 : 이러한 단점 대부분을 해결하는 SharedPreferences를 대체합니다. DataStore에는 Kotlin 코루틴과 Flow를 사용하는 완전 비동기식 API가 포함되어 있으며 데이터 마이그레이션을 처리하고 데이터 일관성을 보장하며 데이터 손상을 처리합니다.
* SharedPreferences에는 UI 스레드에서 안전하게 호출할 수 있는 것처럼 보이지만 실제로는 디스크 I/O 작업을 수행하는 동기 API가 있습니다. 또한 apply()에서 UI 스레드를 차단합니다 fsync(). 보류 중인 fsync()호출은 서비스가 시작되거나 중지될 때마다, 그리고 애플리케이션의 어느 위치에서든 활동이 시작되거나 중지될 때마다 트리거됩니다. UI 스레드는 fsync()에 의해 예약된 보류 중인 호출 에서 차단되며, 종종 ANRapply() 의 소스가 됩니다 .
** SharedPreferences는 런타임 예외로 구문 분석 오류를 발생시킵니다.
두 구현 모두에서 DataStore는 달리 지정하지 않는 한 기본 설정을 파일에 저장하고 모든 데이터 작업을 수행합니다 Dispatchers.IO.
Preferences DataStore와 Proto DataStore 모두 데이터 저장을 허용하지만 서로 다른 방식으로 이를 수행합니다.
- SharedPreferences와 마찬가지로 Preference DataStore에는 스키마를 정의하거나 키가 올바른 유형으로 액세스되는지 확인할 방법이 없습니다.
- Proto DataStore를 사용하면 프로토콜 버퍼를 사용하여 스키마를 정의할 수 있습니다 . Protobufs를 사용하면 강력한 형식의 데이터를 유지할 수 있습니다 . XML 및 기타 유사한 데이터 형식보다 더 빠르고, 더 작고, 더 간단하고, 덜 모호합니다. Proto DataStore에서는 새로운 직렬화 메커니즘을 배워야 하지만 Proto DataStore가 제공하는 강력한 형식의 스키마 이점은 그만한 가치가 있다고 믿습니다.
실제 적용해보자!!
1. 라이브러리 적용
// datastore
implementation("androidx.datastore:datastore-preferences:1.0.0")
2. extension datastore 생성
produceMigrations 값은 기존 sharedPreferences 에서 사용하던 key 값을 넣는다. 없다면 필수x
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
// migration 시 값 추가 필요
name = "datastore 이름",
produceMigrations = { context -> listOf(SharedPreferencesMigration(context, DataStore.PREFERENCES_NAME,
setOf(DataStoreFactory.USER_PHONE_NUMBER, DataStoreFactory.USER_CREATE_DB_FIRST)) }
)
3. 간단하게 사용할 수 있다.
/**
* 번호 저장
*/
suspend fun savePhoneNumberData(phoneNumberData: Long) {
dataStore.storeValue(USER_PHONE_NUMBER, phoneNumberData)
}
suspend fun getPhoneNumberData(): Long {
return dataStore.readValue(USER_PHONE_NUMBER, 0)
}
4. suspend 함수로 되어있어서 코루틴으로 값을 가져와야 하는 단점이 있긴하다.
CoroutineScope(Dispatchers.IO).launch {
var phoneNumber = dataStore.getPhoneNumberData()
}