Android2022. 1. 26. 11:34

DataStore는 SharedPreferences를 대체하기 위해서 고안되었다.
SharedPreferences를 대체해야 겠다는 생각을 구글개발자들은 왜 했을까?


1. SharedPreferences는 UIThread에서 호출이 가능한데 기본적으로 disk I/O operation이기 때문에 UITHread에 대해 block을 유발하게 된다. 때에 따라서는 이점이 anr을 유발할수도 있다.
2. 런타임 파싱에러를 발생시킬 수 있다.

 

이런 문제점이 마음에 들지 않았던 어느 개발자가 DataStore를 만들었다.


DataStore는 두가지 구현을 제공하는데 모두 파일로 저장하고 Dispatchers.IO에서 동작한다.

 

1. Preference DataStore는 SharedPreference와 유사하고 key/value쌍으로 저장한다.
2. Proto DataStore는 Protocol buffer로 스키마를 정의하고 강력한 데이타타입체크를 지원한다. protocol buffer는 더 빠르고 작고 단

순하며 다른 XML같은 포맷보다 덜 모호하다. protocol buffer 정의하고 시리얼화 매커니즘을 추가로 배워야 하지만 강력한 타입스키마체크라는 장점때문에 충분히 배울 가치가 있다고 본다.

Room이라는 또 다른 Store를 제공하는 라이브러리가 있으며 DataStore가 작고 단순한 데이터셋을 지원하는 반면 Room은 부분업데이트나 참조무결성을 지원한다.  정확하게 어떤 다른 특성이 있는지는 더 조사해 보아야 겠다.

// Preferences DataStore 사용시
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"

// Proto DataStore 사용시
implementation  "androidx.datastore:datastore-core:1.0.0-alpha01"

Proto DataStore를 사용하고자 한다면 스키마를 proto file로 /app/src/main/proto 폴더에 저장한다. 스키마정의 링크에서 자세한 정의 방법을 더 숙지해야 한다.

syntax = "proto3";

option java_package = "<your package name here>";
option java_multiple_files = true;

message Settings {
  int my_counter = 1;
}

 

DataStore 생성

Preference DataStore 생성

// with Preferences DataStore
val dataStore: DataStore<Preferences> = context.createDataStore(
    name = "settings"
)

Proto DataStore 생성

protobuff file명과 Serializer의 구현체를 제공해야 한다. 

object SettingsSerializer : Serializer<Settings> {
    override fun readFrom(input: InputStream): Settings {
        try {
            return Settings.parseFrom(input)
        } catch (exception: InvalidProtocolBufferException) {
            throw CorruptionException("Cannot read proto.", exception)
        }
    }

    override fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output)
}


// with Proto DataStore
val settingsDataStore: DataStore<Settings> = context.createDataStore(
    fileName = "settings.pb",
    serializer = SettingsSerializer
)

DataStore 읽기

Preference DataStore 읽기

val MY_COUNTER = preferencesKey<Int>("my_counter")
val myCounterFlow: Flow<Int> = dataStore.data
     .map { currentPreferences ->
        // Unlike Proto DataStore, there's no type safety here.
        currentPreferences[MY_COUNTER] ?: 0   
   }

Proto DataStore 읽기

val myCounterFlow: Flow<Int> = settingsDataStore.data
    .map { settings ->
        // The myCounter property is generated for you from your proto schema!
        settings.myCounter 
    }

 

DataStore 쓰기

Preference DataStore 쓰기

suspend fun incrementCounter() {
    dataStore.edit { settings ->
        // We can safely increment our counter without losing data due to races!
        val currentCounterValue = settings[MY_COUNTER] ?: 0
        settings[MY_COUNTER] = currentCounterValue + 1
    }
}

 

Proto DataStore 쓰기

suspend fun incrementCounter() {
    settingsDataStore.updateData { currentSettings ->
        // We can safely increment our counter without losing data due to races!
        currentSettings.toBuilder()
            .setMyCounter(currentSettings.myCounter + 1)
            .build()
    }
}

 

Data를 Modeling할때 DataStore를 사용해서 해보는것도 좋은 경험이 될것 같다.

언제 해볼 수 있을지 모르겠지만 .. ㅠㅜ 

Posted by 삼스