Proto DataStore : JetPack Lib - Part 2

woga·2021년 10월 18일
0

Android 공부

목록 보기
12/49
post-thumbnail

Proto DataStore

  • 인스턴스를 사용자 지정 데이터로 저장합니다.
  • 프로토콜 버퍼를 사용하여 스키마를 정의합니다.
  • xml 및 기타 유사한 데이터 형식보다 빠르고, 작고, 단순하고, 명확합니다.

프로토콜 버퍼란?

프로토콜 버퍼는 구조화된 데이터를 직렬화하기 위한 Google의 언어 중립적, 플랫폼 중립적, 확장 가능한 메커니즘입니다.
데이터 구조화 방법을 한 번 정의한 다음 특수 생성 소스 코드를 사용하여 다양한 데이터 스트림과 다양한 언어를 사용하여 구조화된 데이터를 쉽게 쓰고 읽을 수 있습니다.

.proto 파일에 포로토콜 버퍼 메세지 타입을 정의하여 우리가 직렬화할 정보가 어떻게 구조화 될지를 기술합니다.
각 프로토콜 버퍼 메시지는 하나의 작은 정보 레코드로서 일련의 이름-값 쌍을 포함합니다.

  • 기본 예제
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

각 메시지 타입은 하나 이상의 유일한 숫자 변수를 갖으며 각 필드는 이름과 값을 갖습니다. 값의 유형으로는 숫자, 불리언, 로우 바이트, 위의 예제와 같이 다른 프로토콜 버퍼 메시지 타입이 될 수 있으며 우리는 우리의 대이터를 계층적으로 구조화 할 수 있습니다.선택적 필드나 필수 필드(required fields), 반복되는 필드들을 기술할 수 있습니다.

장점

  • 크기가 3~10배 더 작다
  • 20~100배 더 빠르다
  • 모호함이 적다
  • 프로그램에서 사용하기 더 쉬운 데이터 접근 클래스들을 생성한다

How to use Proto Datastore?

build.gradle

Proto Datastore를 사용하고 스키마용 코드 생성을 위해 Protobuf를 가져오려면 다음과 같은 작업으로 build.gradle 파일을 조금 변경해야 합니다.

  • Protobuf 플러그인 추가
  • Protobuf 및 Proto Datastore 종속 항목 추가
  • Protobuf 구성
plugins {
    ...
    id "com.google.protobuf" version "0.8.12"
}

dependencies {
    implementation  "androidx.datastore:datastore-core:1.0.0-alpha04"
    implementation  "com.google.protobuf:protobuf-javalite:3.10.0"
    ...
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.10.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'
                }
            }
        }
    }
}

create

이를 만드는 작업은 두 단계로 이루어진다.

  • Searializer<T>를 구현하는 클래스를 정의합니다. 여기서 T는 .proto 파일에 정의된 유형입니다. serializer 클래스는 데이터 유형을 읽고 쓰는 방법을 Datastore에 notify합니다.

  • datasStore로 만든 delegate하여 DataStore<T>의 인스턴스를 만듭니다. 여기서 T.proto 파일에 정의된 유형입니다.

스키마 정의 (proto 파일 만들기)

user_prefs.proto 파일을 새로 만듭니다. protobuf에서 각 구조는 message 키워드를 사용하여 정의됩니다. 구조의 각 요소는 유형과 이름에 따라 메시지 내에 정의되면 1부터 차례로 순서가 할당됩니다. 우선은 showCompleted라는 부울 값이 있는 UserPreferences 메시지를 정의해보겠습니다.

syntax = "proto3";

option java_package = "com.example.application";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

Settings 클래스는 proto 파일에 정의된 message로부터 컴파일 시간에 생성됩니다. 프로젝트를 다시 빌드해야 합니다.


Serializer 만들기

proto 파일에 정의한 데이터 유형을 읽고 쓰는 방법을 Datastore에 알리려면 serializer를 구현해야 합니다. 또한 serializer는 디스크에 데이터가 없는 경우 반환될 기본값을 정의합니다. data 패키지에 SettingsSerializer라는 새 파일을 만듭니다.

object SettingsSerializer : Serializer<Settings> {
  override val defaultValue: Settings = Settings.getDefaultInstance()

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

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

Datastore 만들기

  • Datastore가 동작할 파일 이름
  • Datastore 사용되는 유형을 위한 serializer. 이 경우에는 UserPreferencesSerializer
val Context.settingsDataStore: DataStore<Settings> by dataStore(
  fileName = "settings.pb",
  serializer = SettingsSerializer
)

read

DataStore.data를 사용하여 저장된 객체에서 적절한 속성의 Flow를 expose

val exampleCounterFlow: Flow<Int> = context.settingsDataStore.data
  .map { settings ->
    // The exampleCounter property is generated from the proto schema.
    settings.exampleCounter
  }

write(edit)

트랜잭션 방식으로 업데이트하는 updataData() 함수를 제공합니다. 이 함수는 데이터의 현재 상태를 데이터 유형의 인스턴스를 제공하고 읽기-쓰기-수정 작업을 통해 트랜잭셕 방식으로 데이터를 업데이트합니다

suspend fun incrementCounter() {
  context.settingsDataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
      .setExampleCounter(currentSettings.exampleCounter + 1)
      .build()
    }
}

요약 정리

  • SharedPreferences는 UI 스레드에서 호출하기에 안전한 동기 API가 있고, 오류 신호를 보내는 메커니즘이 없고, 트랜잭션 API가 없다는 등 일련의 단점이 있습니다.

  • SharedPreferences를 대체하는 Datastore는 API의 거의 모든 단점을 해결합니다.

  • Datastore는 Kotlin 코루틴과 Flow를 사용하는 완전 비동기 API가 있으며, 데이터 이전을 처리하고, 데이터 일관성을 보장하고, 데이터 손상을 처리합니다.

더 자세한건 이 링크를 보길..
https://yalematta.dev/blog/proto-datastore.html

Reference

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=sthwin&logNo=221020384012

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

https://medium.com/scalereal/hello-datastore-bye-sharedpreferences-android-part-2-proto-datastore-2716fbfd4783

https://developer.android.com/codelabs/android-proto-datastore#9

profile
와니와니와니와니 당근당근

0개의 댓글