기존 프로젝트에서 서버에서 받은 로그인 토큰값을 간단하게 저장하기 위해 SharedPreferences를 사용하여 저장하였다.
복잡한 대규모 데이터가 아닌, 소규모였기 때문에 Room을 사용하기보다 SharedPreferences를 사용해서 저장했다.
DataStore는 SharedPreferences의 단점을 보완하며 등장했다.
DataStore가 데이터처리에 Flow를 도입했기 때문에 스레드나 예외처리 부분에서 개선되었다.
ui스레드에서 안전하게 call할 수 있고, 예외처리에서도 안전해졌다.
이렇게 여러 가지 단점들을 보완하며 나타난 것이 DataStore이다.
DataStore에는 다음과 같이 두 가지 유형이 존재한다.
이 글에서는 키를 사용하여 데이터를 저장하고 데이터에 엑세스하는 Preferences DataStore를 지금하고있는 프로젝트에 적용해보자.
gradle 파일에 다음을 추가하자.
implementation 'androidx.datastore:datastore-preferences:1.0.0'
이제 책 정렬 방식을 정확도순으로 할 것인지, 최근 순으로 할 것인지 DataStore에 저장해두고, 앱을 다시 시작했을 때
기존에 있던 방식을 그대로 출력하는 것을 구현해보자.
enum class Sort(val value : String) {
ACCURACY("accuracy"),
LATEST("latest")
}
먼저, DataStore에 저장할 데이터가 담긴 enum클래스를 만들어준다.
여기서 코루틴의 flow를 사용해 데이터를 추출할 수 있도록 하는 DataStore의 특성을 이용해 다음과 같이 구현해준다.
private object PreferencesKeys{
val SORT_MODE = stringPreferencesKey("sort_mode")
}
정렬모드는 string타입이기 때문에 Key값을 string으로 저장한다.
override suspend fun saveSortMode(mode: String) {
dataStore.edit {prefs ->
prefs[SORT_MODE] = mode
}
}
정렬방식이 달라질 때 마다 saveSortMode가 실행되고 이는, SORT_MODE를 바꾼 mode로 설정한다.
override suspend fun getSortMode(): Flow<String> {
return dataStore.data.catch { exception ->
if(exception is IOException){
exception.printStackTrace()
emit(emptyPreferences())
}else{
throw exception
}
}
.map{ prefs ->
prefs[SORT_MODE] ?: Sort.ACCURACY.value
}
}
파일에 접근하기 위해 data 메서드를 사용하고, 실패했을 때를 대비하여 catch를 활용한 예외처리까지 해주었다.
map 안에 키를 전달해서 플로우를 반환받는다. 이 때 기본값은 정확도순의 정렬방식으로 해주었다.
Repository에서 구현해주었으니 다음 ViewModel에서 이를 불러오는 부분을 구현해준다.
fun saveSortMode(value : String) = viewModelScope.launch(Dispatchers.IO) {
bookSearchRepository.saveSortMode(value)
}
suspend fun getSortMode() = withContext(Dispatchers.IO){
bookSearchRepository.getSortMode().first()
}
전체 데이터 스트림을 구독할 필요가 없고 단일값을 가져오기 때문에 first를 이용하였다.
또한, 반드시 값을 반환하고 종료되기 때문에 withContext로 구현하였다.
이제 MainActivity에서 Repository로 DataStore로 전달해야하기 때문에 다음과 같이 전달해주자.
private val Context.dataStore by preferencesDataStore(DATASTORE_NAME)
val bookSearchRepository = BookSearchRepositoryImpl(database, dataStore)
이제 해당 프래그먼트에서 사용할 부분을 구현해주자.
해당 체크버튼이 바뀔때마다 데이터에 저장을하고, 앱을 시작했을 때 사용자가 마지막으로 설정했던 정렬방식이 그대로 남아있도록 Load하는 메소드를 구현하면 끝!!
//체크된 버튼을 확인한 후, Sort값을 받아와서 viewmodel에 저장함
private fun saveSettings(){
binding.rgSort.setOnCheckedChangeListener{_, checkedId->
val value = when(checkedId){
R.id.rb_accuracy -> Sort.ACCURACY.value
R.id.rb_latest -> Sort.LATEST.value
else -> return@setOnCheckedChangeListener
}
bookSearchViewModel.saveSortMode(value)
}
}
//불러온 값을 확인 후 라디오버튼에 반영함.
private fun loadSettings(){
lifecycleScope.launch{
val buttonId = when(bookSearchViewModel.getSortMode()){
Sort.ACCURACY.value -> R.id.rb_accuracy
Sort.LATEST.value -> R.id.rb_latest
else -> return@launch
}
binding.rgSort.check(buttonId)
}
}
체크할때마다 저장값을 바꾸어 viewModel에 save메소드에 value값을 넣어주었고,
현재 저장되어있는 값을 불러와 해당 값에 해당하는 라디오버튼에 체크가 되게끔 구현하였다.
따라서 정렬방식을 기본값이 아닌 최근 순으로 바꾸고 종료하고 다시 시작했을 때 다음과 같이 나올 수 있었다!
'Android > JETPACK' 카테고리의 다른 글
[JETPACK개론] Paging (0) | 2022.09.26 |
---|---|
[JETPACK개론] Navigation (0) | 2022.09.18 |
[JETPACK개론] WorkManager(5) + 주기적작업, 고유작업 (0) | 2022.09.17 |
[JETPACK개론] WorkManager(4) + 작업진행률 관찰 (0) | 2022.09.14 |
[JETPACK개론] WorkManager(3) + Chaining, Coroutine (0) | 2022.09.09 |