본문 바로가기
Android/simple data 저장하기

datastore-protobuf 사용하기

by startSW 2023. 8. 30.

https://github.com/swjsw/ProtoDatastoreExample

 

GitHub - swjsw/ProtoDatastoreExample

Contribute to swjsw/ProtoDatastoreExample development by creating an account on GitHub.

github.com

 

datastore-protobuf 모듈은 Android의 Jetpack DataStore 라이브러리의 하위 모듈 중 하나로, Protocol Buffers를 사용하여 데이터를 저장하고 관리하는 데 사용되는 기능을 제공합니다. datastore-protobuf 모듈은 데이터를 Protocol Buffers 형식으로 직렬화하고 저장하며, 데이터의 유지 관리와 효율적인 변경 추적을 위해 사용됩니다.

 

Protocol Buffers는 Google에서 개발한 직렬화 데이터 포맷으로, 데이터 크기가 작고 직렬화/역직렬화 속도가 빠르며, 가독성이 좋지 않은 JSON이나 XML과 달리 구조화된 이진 데이터 형식을 사용합니다.

 

datastore-protobuf 모듈을 사용하여 데이터를 저장하고 관리하는 방법은 다음과 같습니다:

 

1. Gradle에 DataStore 의존성 추가하기 먼저, app의 build.gradle 파일에 DataStore의 의존성과 Protocol Buffers의 의존성을 추가해야 합니다.

plugins {
    ...
    id "com.google.protobuf" version "0.9.1"
}

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.19.4"
    }

    // 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().forEach { task ->
            task.builtins {
                create("java") {
                    option("lite")
                }
            }
        }
    }
}

  • Error 해결 방법
    • protobuf 버전을 “0.8.17”을 사용하게 되면 아래의 에러가 발생합니다.
      • A problem occurred configuring project ':app'.
        Could not get unknown property 'source' for generate-proto-generateDebugProto of type org.gradle.api.internal.file.DefaultSourceDirectorySet. 
      • e: /Users/juj/work/tistory/ProtoDatastoreExample/app/build.gradle.kts:54:5: Unresolved reference: protoc
      • 해결 방법 : protobuf 버전을 “0.9.1”로 변경
    • gradle에 “protobuf { }” 를 추가하지 않으면 아래의 에러가 발생합니다.
      • Cause: error=2, No such file or directory
      • 해결 방법 : protobuf { } 를 추가해 줍니다.

 

2. Proto 파일 정의하기: 데이터의 구조를 Protocol Buffers로 정의하는 .proto 파일을 작성해야 합니다. 이 파일은 데이터 구조를 정의하고 직렬화/역직렬화할 수 있는 코드를 생성합니다.

syntax = "proto3";

option java_package = "com.swjsw.example.protodatastore.data.repository.user";
option java_multiple_files = true;

message UserPreferences {
  // filter for showing / hiding completed tasks
  string name = 1;
  string email = 2;
}

.proto 파일은 프로젝트의 리소스 디렉토리에 저장됩니다. 보통 src/main/proto 디렉토리 아래에 위치시키는 것이 일반적입니다. 이 디렉토리는 Android Gradle 프로젝트의 소스 디렉토리 구조에 포함되는 곳으로, 프로토 파일을 포함한 리소스 파일들을 저장하는데 사용됩니다.

다음은 프로젝트 구조에서 .proto 파일을 저장하는 위치의 예시입니다:

파일명은 sample.proto 입니다.

app
├── src
│   ├── main
│   │   ├── proto
│   │   │   ├── sample.proto
│   │   ├── java
│   │   ├── res
│   │   ├── ...

위의 예시에서 sample.proto 파일은 src/main/proto 디렉토리에 위치합니다. 이렇게 하면 Gradle 빌드 과정에서 .proto 파일이 컴파일되어 Kotlin 코드로 생성됩니다. 생성된 Kotlin 코드를 사용하여 Protocol Buffers 데이터를 다룰 수 있게 됩니다.

 

 

3. Serializer 만들기 proto 파일에 정의한 데이터 유형을 읽고 쓰는 방법을 Datastore에 알리려면 serializer를 구현해야 합니다. 또한 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)
}

 

 

4. Proto DataStore 모듈 만들기

@InstallIn(SingletonComponent::class)
@Module
object DataStoreModule {
    @Singleton
    @Provides
    fun provideProtoDataStore(@ApplicationContext appContext: Context): DataStore<UserPreferences> {
        return DataStoreFactory.create(
            serializer = UserPreferencesSerializer,
            produceFile = { appContext.dataStoreFile(DATA_STORE_FILE_NAME) },
            corruptionHandler = null
        )
    }
}

 

 

5. Proto DataStore로 데이타 읽고 / 쓰기

override val userName: Flow<String> = 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 ->
            Log.d("datastore", "flowtest UserRepositoryImpl : userName : ${preferences.name}")
            if (preferences.name.isNullOrEmpty()) {
                DEFAULT_USER_NAME
            } else preferences.name
        }
//            .flowOn(Dispatchers.IO) // datastore 는 기보적으로 IO 스레드에서 실행되지만 여기서는 명시적으로 지정, 없어도 무방함

    override suspend fun getUserName(): String {
        return userName.first()
    }

    override suspend fun setUserName(name: String) {
        userPreferencesStore.updateData { preferences ->
            preferences.toBuilder().setName(name).build()
        }
    }

DataStore 모듈과 사용 방법에 대해 자세한 설명은 다음에 추가할 예정입니다.

DataStroe 를 사용하는 예제는 아래의 링크에 있습니다.

아래 예제는 Hilt, ViewBinding, DataBiding, AAC ViewModel을 사용했고, MVVM 패턴의 구조를 가지고 있습니다.

 

https://github.com/swjsw/ProtoDatastoreExample

 

GitHub - swjsw/ProtoDatastoreExample

Contribute to swjsw/ProtoDatastoreExample development by creating an account on GitHub.

github.com

 

'Android > simple data 저장하기' 카테고리의 다른 글

Datastore - 구글 설명  (0) 2023.08.31
datastore-preferences 사용하기  (0) 2023.08.30
DataStore 란?  (0) 2023.08.30
SharedPreferences 사용하기  (0) 2023.08.29
simple data 저장 방법  (0) 2023.08.29