본문

DataStore 개념 및 실습

도입

이번 포스팅에서는 DataStore 에 대해 정리할 예정이다.

DataStore 개념 및 특징

  • Jetpack에서 제공하는 key-value 데이터 저장소
  • Coroutine, Flow을 지원해 데이터를 asynchronously, consistently, transactionally하게 저장 할 수 있다.

DataStore VS SharedPreference

DataStore의 종류

DataStore는 아래와 같이 2가지 종류가 있다.

Preferences DataStore

keys를 통한 데이터 접근, type safety 제공 X

// Preferences DataStore (SharedPreferences like APIs)
dependencies {
  implementation "androidx.datastore:datastore-preferences:1.0.0-alpha05"
}
// Alternatively - use the following artifact without an Android dependency.
dependencies {
  implementation "androidx.datastore:datastore-preferences-core:1.0.0-alpha05"
}

Proto DataStore

custom data type을 통한 데이터 접근
(Protocol buffers에 schema 저장 필요), type safety 제공 O

// Typed DataStore (Typed API surface, such as Proto)
dependencies {
  implementation "androidx.datastore:datastore:1.0.0-alpha05"
}
// Alternatively - use the following artifact without an Android dependency.
dependencies {
  implementation "androidx.datastore:datastore-core:1.0.0-alpha05"
}

실습

Preferences DataStore

// Read Preferences DataSource
fun setRecentStayedPackageByPrefDataStore(
    componentActivity: ComponentActivity,
    packageName: String
) {
    // Create Preferences DataStore
    val preferenceDataStore = componentActivity.createDataStore(name = "testSettings")
    val recentStayPackageKey = preferencesKey<String>(KEY_RECENT_STAYED_PACKAGE)
    componentActivity.lifecycleScope.launch(Dispatchers.IO) {
        preferenceDataStore.edit { testSettings ->
            testSettings[recentStayPackageKey] = packageName
        }
    }
}

// Write to a Preferences DataStore
fun getRecentStayedPackageByPrefDataStore(
    componentActivity: ComponentActivity
): String? {
    // Create Preferences DataStore
    val preferenceDataStore = componentActivity.createDataStore(name = "testSettings")
    val recentStayPackageKey = preferencesKey<String>(KEY_RECENT_STAYED_PACKAGE)
    return runBlocking {
        preferenceDataStore.data
            .map { preferences -> preferences[recentStayPackageKey] }
            .firstOrNull()
        }
}

Proto DataStore

// Apply plugin
// @see https://jitpack.io/p/google/protobuf-gradle-plugin
plugins {
    id "com.google.protobuf" version "0.8.12"
}

dependencies {
    implementation "com.google.protobuf:protobuf-javalite:3.11.0"
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.11.0"
    }

    generateProtoTasks {
        all().each { task ->
            task.builtins {
                java {
                    option 'lite'
                }
            }
        }
    }
}
// app/srs/main/proto/user_pref.proto
syntax = "proto3";

option java_package = "$YourPackageName";
option java_multiple_files = true;

message UserPreferences {
    string recent_stayed_package = 1;
}

// Serialize protoBuffer
object DataStoreSerializer : Serializer<UserPreferences> {
    override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()

    override fun readFrom(input: InputStream): UserPreferences {
        try {
            return UserPreferences.parseFrom(input)
        } catch (exception: InvalidProtocolBufferException) {
            throw CorruptionException("Cannot read proto.", exception)
        }
    }

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

// Read from a proto DataStore
fun setRecentStayedPackageByProtoDataStore(
        componentActivity: ComponentActivity,
        packageName: String
    ) {
        val protoDataStore: DataStore<UserPreferences> = componentActivity.createDataStore(
            fileName = "user_pref.pb",
            serializer = DataStoreSerializer
        )
        componentActivity.lifecycleScope.launch(Dispatchers.IO) {
            protoDataStore.updateData { currentUserPref ->
                currentUserPref.toBuilder()
                    .setRecentStayedPackage(packageName)
                    .build()
            }
        }
    }

// Write to a proto DataStore
fun getRecentStayedPackageByProtoDataStore(componentActivity: ComponentActivity): String {
        val protoDataStore: DataStore<UserPreferences> = componentActivity.createDataStore(
            fileName = "user_pref.pb",
            serializer = DataStoreSerializer
        )
        return runBlocking {
            protoDataStore.data
                .map { currentUserPref -> currentUserPref.recentStayedPackage }
                .first()
        }
    }

DataStore in synchronous code

Synchronous code를 제공하기 위해 코루틴의 runBlocking 사용 가능
추가로 아래 스냅핏을 통해 메모리 캐시에 로드 후 runBlocking 사용 권고
(빠르게 사용하기 위함)

override fun onCreate(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        dataStore.data.first()
    }
}

Reference

https://developer.android.com/topic/libraries/architecture/datastore#typed-datastore

https://scalereal.com/android/2020/09/03/hello-datastore-bye-sharedpreferences-android-part-2-proto-datastore.html

https://developers.google.com/protocol-buffers/docs/javatutorial

'컴퓨터 > 이론: 안드로이드' 카테고리의 다른 글

Unit Test 기본 실습  (0) 2020.12.20
Unit Test 개념 및 Lifecycle  (0) 2020.12.19
Gradle implementation  (0) 2020.04.02
generated Folder 란?  (0) 2020.03.16
minSdkVersion, targetSdkVersion, compileSdkVersion 정리  (0) 2020.03.14

공유

댓글