-
안드로이드 DataStore 예제안드로이드 학습/Android 기술면접 대비 2025. 2. 28. 18:01
DataStore 설명 : 링크
DataStore에는 2가지 종류가 있다.
- 기본 타입을 저장하려면 Preference Datastore :
- 기본 타입 이외의 타입을 저장하려면 Proto Datastore를 사용해야 한다.
Preferences DataStore 예제
1. build.gradle (Modulo:app)
implementation "androidx.datastore:datastore-preferences:1.1.3" implementation "androidx.datastore:datastore-preferences-rxjava2:1.1.3" // optional - RxJava2 support implementation "androidx.datastore:datastore-preferences-rxjava3:1.1.3" // optional - RxJava3 support
optional 부분은 Flow를 RxJava2로 변환할 필요 없이 바로 사용할 수 있게 지원한다.
2. Datastore 인스턴스를 만들기 위해 preferencesDataStore 위임을 사용하며 수신기로 Context를 사용
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
3. 저장하려는 데이터 타입과 key
private val stringKey = stringPreferencesKey("key_name") // string
데이터 타입에 따른 키 세팅
더보기private val stringKey = stringPreferencesKey("string_key") // String 저장
private val intKey = intPreferencesKey("int_key") // Int 저장
private val booleanKey = booleanPreferencesKey("boolean_key") // Boolean 저장
private val floatKey = floatPreferencesKey("float_key") // Float 저장
private val doubleKey = doublePreferencesKey("double_key") // Double 저장
private val longKey = longPreferencesKey("long_key") // Long 저장
4. 데이터 저장
// Preference에 Setting 하는 방법 suspend fun setText(text: String) { context.dataStore.edit { preferences -> preferences[stringKey] = text } }
- suspend를 사용해 코루틴 비동기 방식으로 데이터 저장이 된다.
- preferences[stringKey] 부분에서 stringKey는 앞서 3번에서 만든 key를 넣엇 데이터를 저장한다.
5. 데이터 호출
val testString: Flow<String> = context.dataStore.data .catch { exception -> if (exception is IOException) { emit(emptyPreferences()) } else { throw exception } } .map { preferences -> preferences[stringKey] ?: "" }
- 데이터 호출 과정에서 예외처리가 가능하다. (catch{})
- map을 통해 데이터 가공할 수 있고 최종적으로 Flow<> 형식으로 반환해준다.
6. Application Class
class AioApplication : Application() { private lateinit var dataStore: DataStoreUtil companion object { private lateinit var instance: AioApplication fun getInstance(): AioApplication = instance fun getAppContext(): Context = instance.applicationContext } override fun onCreate() { super.onCreate() instance = this dataStore = DataStoreUtil(this) } fun getDataStore() : DataStoreUtil = dataStore }
싱글톤 방식으로 앱 전체에서 사용할 수 있게 만들기 위해서 Application class에 선언해주고 호출도 해줄 수 있게 만들었다.
7. ViewModel 부분
private val dataStoreUtil = AioApplication.getInstance().getDataStore() val dataStoreTestFlow: StateFlow<String> = dataStoreUtil.testString .stateIn( viewModelScope, SharingStarted.Lazily, "" ) fun saveText(text: String) { viewModelScope.launch { dataStoreUtil.setText(text) } }
8. Fragment
viewLifecycleOwner.lifecycleScope.launch { viewModel.dataStoreTestFlow.collectLatest { text -> binding.tvDatastoreShowText.text = text } }
반응형Proto Datastore 예제 :
Proto Datastore는Preferences DataStore에 비해 추가적인 과정이 필요하다
1. build.gradle (Modulo:app)
plugins { ... 생략 ... id "com.google.protobuf" }
dependencies { implementation "androidx.datastore:datastore-core:1.0.0" implementation "com.google.protobuf:protobuf-javalite:3.18.0" ... } protobuf { protoc { artifact = "com.google.protobuf:protoc:3.14.0" } // Generates the java Protobuf-lite code for the Protobufs in this project. See // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation // for more information. generateProtoTasks { all().each { task -> task.builtins { java { option 'lite' } } } } }
2. 파일이름.proto 생성
app/src/main/proto 디렉터리에 user_prefs.proto라는 새 파일을 만듭니다.
syntax = "proto3"; option java_package = "com.aio.kotlin.data.datastore"; option java_multiple_files = true; message UserPreferences { // filter for showing / hiding completed tasks bool show_completed = 1; string username = 2; }
option java_package = "com.aio.kotlin.data.datastore";
- proto 파일을 컴파일하면 해당 패키지(com.aio.kotlin.data.datastore) 아래에 자동으로 클래스를 생성하게 된다.
bool show_completed = 1;
- 여기서 1은 필드 번호 (Field Number) 를 의미 하고 데이터를 송수신할 때 필드 번호 기반으로 매핑됨.
3.serializer 만들기
object UserPreferencesSerializer : Serializer<UserPreferences> { override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance() override suspend fun readFrom(input: InputStream): UserPreferences { try { return UserPreferences.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override suspend fun writeTo(t: UserPreferences, output: OutputStream) = t.writeTo(output) }
위에 설정한 위치에 serializer 파일을 만들었다.
option java_package = "com.aio.kotlin.data.datastore";
4. DataStore
// Proto DataStore private val Context.userPreferencesStore: DataStore<UserPreferences> by dataStore( fileName = "user_prefs.pb", serializer = UserPreferencesSerializer )
5. 데이터 저장
suspend fun updateShowCompleted(completed: Boolean, data: String) { context.userPreferencesStore.updateData { preferences -> preferences .toBuilder() .setShowCompleted(completed) .setUsername(data) .build() } }
6. 데이터 호출
val userPreferencesFlow: Flow<ProtoDataStore> = context.userPreferencesStore.data .catch { exception -> // dataStore.data throws an IOException when an error is encountered when reading data if (exception is IOException) { emit(UserPreferences.getDefaultInstance()) } else { throw exception } } .map { preferences -> // Get our show completed value, defaulting to false if not set: val showCompleted = preferences.showCompleted val name = preferences.username ProtoDataStore(showCompleted, name) }
7. ViewModel
class DataStoreViewModel : ViewModel() { private val dataStoreUtil = AioApplication.getInstance().getDataStore() val newDataTestFlow: StateFlow<ProtoDataStore> = dataStoreUtil.userPreferencesFlow .stateIn( viewModelScope, SharingStarted.Lazily, ProtoDataStore(false, "") ) fun saveProtoDataStore(data: String) { viewModelScope.launch { dataStoreUtil.updateShowCompleted(true, data) } } }
8. Fragment
viewLifecycleOwner.lifecycleScope.launch { viewModel.newDataTestFlow.collectLatest { text -> binding.tvDatastoreShowText.text = text.name } }
종합적으로 봤을때 DataStoreUtil은 아래와 같다.
// Preferences DataStore val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings") // Proto DataStore private val Context.userPreferencesStore: DataStore<UserPreferences> by dataStore( fileName = "user_prefs.pb", serializer = UserPreferencesSerializer ) class DataStoreUtil(private val context: Context) { private val stringKey = stringPreferencesKey("key_name") // string // Preferences DataStore 데이터 얻기 val testString: Flow<String> = context.dataStore.data .catch { exception -> if (exception is IOException) { emit(emptyPreferences()) } else { throw exception } } .map { preferences -> preferences[stringKey] ?: "" } // Preferences DataStore 데이터 세팅 suspend fun setText(text: String) { context.dataStore.edit { preferences -> preferences[stringKey] = text } } // Proto Datastore 데이터 얻기 val userPreferencesFlow: Flow<ProtoDataStore> = context.userPreferencesStore.data .catch { exception -> // dataStore.data throws an IOException when an error is encountered when reading data if (exception is IOException) { emit(UserPreferences.getDefaultInstance()) } else { throw exception } } .map { preferences -> // Get our show completed value, defaulting to false if not set: val showCompleted = preferences.showCompleted val name = preferences.username ProtoDataStore(showCompleted, name) } // Proto Datastore 데이터 세팅 suspend fun updateShowCompleted(completed: Boolean, data: String) { context.userPreferencesStore.updateData { preferences -> preferences .toBuilder() .setShowCompleted(completed) .setUsername(data) .build() } } }
'안드로이드 학습 > Android 기술면접 대비' 카테고리의 다른 글
안드로이드 DataStore와 SharedPreference (0) 2025.02.19 안드로이드 아키텍처 (MVC) (0) 2024.12.09 안드로이드 앱 테스트 기본 - 2 (UI Test예제 포함) (0) 2024.12.07 안드로이드 Coroutine Flow - 3 (StateFlow) (0) 2024.11.28 안드로이드 Coroutine Flow - 2 (Flow 사용) (0) 2024.11.28