본문 바로가기
Android/Android Development

DataStore of Android Jetpack

by 2Plus 2021. 10. 24.

Photo by Joshua Sortino on Unsplash

DataStore란?

  • Data Storage Solution이다.
  • Kotlin으로 만들어졌으며, Coroutines와 Flow를 정식 지원하여 async한 작업이 가능
  • SharedPreferences의 새 버전이라고 생각해도 된다.

 

Why DataStore?

  • Kotlin, Coroutines, Flow 정식 지원
  • SharedPreferences는 sync API를 지원하며, Main-thread-safe하지 않다. DataStore는 Dispatchers.IO를 사용하기 때문에 Main-thread에서 사용하기에 안전하다.
  • runtime exception들로부터 안전하다.
  • SharedPreferences로부터의 migration을 제공
  • Type Safety 제공

 

반응형

 

Implementation

  • DataStore는 데이터를 저장하기 위한 두 가지 타입의 implementation을 제공한다.
    • Preference DataStore
      • key-value pair (SharedPreferences와 유사)
      • type safety 미지원
    • Proto DataStore
      • Protocol Buffers라는 것을 사용한 scheme를 통해 커스텀 타입으로 데이터를 저장

 

기능 SharedPreferences Preferences DataStore Proto DataStore
Asynchronous API Listener를 통해 read만 가능 Flow Flow
Synchronous API 지원하지만 UI Thread에서 안전하지 않음 X X
Main-thread-safety X
(Main Thread에서 호출 가능한 Sync API에 I/O 작업이 포함되어 있음)
O
(내부에서 Dispatchers.IO에서 작업)
O
(내부에서 Dispatchers.IO에서 작업)
오류 전송 가능 X O O
runtime-exception safety X
(파싱 오류를 runtime exception 처리)
O O
strong consistency가 보장되는 transaction API 존재 X O O
type safety X X O (with Protocol Buffers)

 

Room과 Datastore 비교

 부분 업데이트, 참조 무결성 또는 대규모/복잡한 데이터 세트가 필요한 경우에는 Datastore 대신 Room을 사용하는 것이 좋다. Datastore는 소규모 또는 단순한 데이터 세트에 적합하며 부분 업데이트나 참조 무결성은 지원하지 않는다.

 

반응형

 


Preferences DataStore

먼저, Preferences DataStore를 사용하기 위한 dependency를 위와 같이 추가해준다.

 

 example을 만들어보기 위해 사용하는 다른 dependency들도 추가해준다.

 

 

사용

 먼저, UI Mode 관련하여 데이터를 관리할 UIModePreference class를 생성해주고, companion object에 상수를 선언해준다. 이번 예제에서는 night mode 관련하여 저장을 할 것이어서 IS_NIGHT_MODE key를 추가했다.

 

 위와 같이 dataStore를 설정해준다.

 

 Preferneces DataStore에서 사용할 키도 위와 같은 형태로 선언해준다. 관리할 데이터의 타입에 맞게 사용하면 된다. 이번 경우에는 boolean을 저장할 것이어서 booleanPreferencesKey를 사용했다.

 

 

사용 - 값 쓰기

 data를 저장할 때는 위와 같이 하면 된다. preferences를 업데이트하기 위해 MutablePreferences를 받아오고, key-value pair 형태로 데이터를 수정하면 된다. 잘 보면 suspend 함수로 선언되어 있는 것을 알 수 있다. dataStore.edit이 suspend 함수여서 그렇다. 내부에서 I/O 작업을 하는 동안 Dispatchers.IO에서 동작하도록 되어 있다. 혹시 Coroutines를 모른다면 이 부분은 넘어가도 좋다. (그래도 모른다면 Coroutines도 공부해놓자)

 

사용 - 값 읽기

 data를 읽을 때는 위와 같이 하면 된다. 앞에서 edit()를 사용하던 것과 다르게 data를 사용하면 된다. MutablePreferences가 아니므로 수정은 불가능하고 읽기만 가능하다. 마찬가지로 key-value pair이기 때문에 key를 이용하여 값을 가져오면 된다. 반환되는 타입은 Flow임을 확인하자.

 

 값을 읽는 중에 작업이 실패하면 IOException이 발생한다. 이러한 경우 빈 값을 전달하기 위해 catch block을 추가했다.

 

 

사용

 이제 이게 잘 동작하는 지 확인하기 위해서 레이아웃, activity를 만들어서 확인하려 한다. 그 전에 ViewModel을 하나 만들어서 isNightMode를 업데이트 하거나 읽어오는 걸 추가한다.

 

 

 

 Activity에서 toggle 버튼을 통해 night mode를 토글할 수 있도록 하고, isNightMode 값에 따라 배경색과 TextView의 메시지 및 색상을 변경하도록 했다. DataStore를 통해 값이 잘 저장되고 Flow type으로 값을 계속 읽어오는 것을 확인할 수 있다. nightMode를 켠 뒤에 앱을 종료하고 다시 실행하면 true 값으로 잘 가져오며 동작하는 것도 확인할 수 있다.

 

반응형

Proto DataStore

 

시작하기 전 Protocol Buffers 간단 이해

 프로토콜 버퍼는 구조화된 데이터를 직렬화하기 위한 Google의 언어 중립, 플랫폼 중립, 확장 가능한 메커니즘이다. XML과 비슷하지만 더 작고 빠르며 간단하다. 데이터를 한 번 구조화하는 방법을 정의한 다음 특수 생성된 소스 코드를 사용하여 구조화된 데이터를 다양한 데이터 스트림에 쉽게 쓰고 읽을 수 있으며 다양한 언어를 사용할 수 있다.

참고 : https://developers.google.com/protocol-buffers?hl=ko

 

dependencies 

 

Schema 정의

 app/src/main/proto 경로에 ui_mode_info.proto 스키마 파일 생성

 

Serializer 만들기

 proto 파일에 정의한 데이터 유형을 읽고 쓰는 방법을 DataStore에 알리기 위해 Serializer 구현

 

사용

 앞서 Preferences DataStore에서 진행한 것을 Proto DataStore에서는 어떻게 하는지에 대한 정리이다.

 DataStore가 동작할 파일 이름과 DataStore에 사용되는 유형을 위한 Serializer를 지정해준다.

 

사용 - 값 쓰기

 값을 쓸 수 있도록 updateData()를 제공해준다. 저장된 데이터를 정의해둔 타입의 인스턴스로 받을 수 있다. 이를 업데이트 하기 위해서 빌더로 변환하고 값을 변경한 후에 build를 해준다. updateData()는 읽기-쓰기-수정 작업을 통해 transaction 방식으로 데이터를 업데이트하고, 데이터가 디스크에 다 써지면 coroutines 작업이 완료된다.

 

사용 - 값 읽기

 dataStore.data를 통해 저장된 데이터를 Flow Type으로 읽을 수 있다. Preferences DataStore와 마찬가지로 데이터를 읽을 때 발생할 수 있는 예외 처리를 해줄 수 있다.

 

 

 동작은 Preferences DataStore와 동일하게 잘 되므로 영상은 생략한다.

 

반응형

Migration

 SharedPreferences에서 Migration이 필요하다면 아래 문서 쪽 확인해보면서 진행하면 된다.

https://developer.android.com/reference/kotlin/androidx/datastore/migrations/SharedPreferencesMigration

반응형

'Android > Android Development' 카테고리의 다른 글

Android ViewModel에서 Context를 올바르게 사용하는 방법  (592) 2022.08.08
LiveData와 Kotlin의 Flow  (956) 2021.11.14
Android LiveData setValue vs postValue  (2) 2021.10.21
Android Custom Lint Checks  (1427) 2021.08.22
Google I/O '21  (1785) 2021.06.20

댓글