Computer >> 컴퓨터 >  >> 시스템 >> Android

Android Proto DataStore:SharedPreferences에서 전환할 가치가 있나요?

Android Proto DataStore:SharedPreferences에서 전환할 가치가 있나요?

몇 년 전 Google은 이미 검증된 SharedPreferences를 대체하는 DataStore를 발표했습니다.

애플리케이션에서 SharedPreferences를 사용했거나 사용한 적이 있다면 전환을 고려할 수도 있습니다. 하지만 모든 것과 마찬가지로 여기서 가장 중요한 질문은 개발 비용이 얼마나 될 것인가입니다.

DataStore를 사용하면 이점이 있지만 Proto DataStore만 사용 가능합니다. 유형 안전성을 제공하면서 객체를 저장할 수 있습니다.

Proto DataStore에 대한 문서를 보면 약간 오래되었으며 작업할 때 몇 가지 중요한 단계가 누락되어 있음을 알 수 있습니다. 그래서 이 글에서는 Proto DataStore를 애플리케이션에 통합하는 방법을 살펴보고 이를 사용하는 것이 그렇게 번거롭지 않다는 것을 보여줄 것입니다.

데이터 저장소란 무엇인가요?

Jetpack DataStore에는 두 가지 변형이 있습니다:

  • 환경설정 데이터 저장소
  • 프로토 데이터스토어

첫 번째에 대해서는 SharedPreferences와 유사하고 널리 다루어졌기 때문에 논의하지 않겠습니다. 이제 Proto DataStore의 Proto가 무엇을 의미하는지 알아보겠습니다.

Proto는 프로토콜 버퍼를 나타내기 위해 Google이 선택한 이름입니다. 이는 구조화된 데이터를 직렬화하는 데 도움이 되는 (Google의) 메커니즘입니다. 언어별로 코딩하는 것이 아니며 일반적으로 작업하려는 데이터 유형을 정의하면 데이터를 읽고 쓰는 데 도움이 되는 코드가 생성됩니다.

✋ 이 글에서는 Proto 3 버전을 사용할 것입니다.

그 정의는 어떻게 생겼나요?

message MyItem {
 string itemName = 1;
 int32 itemId = 2;
}

먼저 message 키워드를 사용하여 객체를 정의합니다. 그 안에는 해당 개체와 관련된 필드가 나열됩니다. 각 필드 끝에 있는 숫자는 필드 자체를 식별하는 데 사용되며 한 번 설정하고 개체를 사용 중이면 변경할 수 없습니다 .

하지만 .proto 파일에 여러 개체를 갖고 싶다면 어떻게 해야 할까요? 개체가 서로 관련되어 있다고 가정하면 메시지 개체를 더 추가하면 됩니다.

message MyItem {
 string itemName = 1;
 int32 itemId = 2;
}
message MyListOfItems {
 repeated MyItem items = 1;
}

위에서 정의한 MyItem 개체에 의존하는 또 다른 메시지 개체를 추가했다는 점에 유의하세요. 개체 목록을 정의하려면 반복을 사용해야 합니다. 키워드.

Proto 데이터 저장소 설정 방법

시작하려면 애플리케이션 수준 build.gradle에 다음 종속성을 추가해야 합니다.

 implementation "androidx.datastore:datastore-preferences:1.0.0"
 implementation "com.google.protobuf:protobuf-javalite:3.18.0"

그런 다음 프로젝트 내에 proto 디렉터리를 만들어야 합니다. 이 디렉터리는 프로젝트 구조에서 Java 폴더의 형제여야 합니다.

proto 디렉터리 내부에 .proto 파일이 생성됩니다. 이 파일은 Proto DataStore에 저장하려는 데이터 유형을 생성하는 역할을 합니다.

Android Proto DataStore:SharedPreferences에서 전환할 가치가 있나요?

proto 디렉터리 내에서 .proto 확장자를 가진 파일을 만듭니다. .proto 파일에는 Todo 목록을 나타내는 개체가 저장됩니다(또 무엇이 있을까요?). 따라서 파일 이름을 todo.proto로 지정하겠습니다. 그러면 다음과 같이 보일 것입니다:

syntax = "proto3";
option java_package = "com.yourPackageName.todo";
option java_multiple_files = true;
message TodoItem {
 string itemId = 1;
 string itemDescription = 2;
}
message TodoItems {
 repeated TodoItem items = 1;
}

두 개의 메시지 객체를 어떻게 정의했는지 주목하세요:

  1. TodoItem – 할 일 항목을 정의합니다.
  2. TodoItems – TodoItem 개체 목록을 정의합니다.

다음으로, TodoItem 및 TodoItems에 대한 클래스가 생성되도록 프로젝트를 빌드합니다.

데이터 객체를 정의한 후에는 이를 직렬화하기 위한 클래스를 생성해야 합니다. 이 클래스는 데이터 저장소에 객체를 읽고 쓰는 방법을 알려줍니다.

// 1
object TodoItemSerializer: Serializer<TodoItems> {
 // 2
 override val defaultValue: TodoItems = TodoItems.getDefaultInstance()
 // 3
 override suspend fun readFrom(input: InputStream): TodoItems {
 try {
 return TodoItems.parseFrom(input)
 } catch (exception: InvalidProtocolBufferException) {
 throw CorruptionException("Cannot read proto.", exception)
 }
 }
 // 3
 override suspend fun writeTo(
 t: TodoItems,
 output: OutputStream
 ) = t.writeTo(output)
}

이번 수업에서 배운 내용을 복습해 보겠습니다.

  1. 클래스를 선언할 때 직렬 변환기를 구현해야 합니다. (T) 유형으로 객체와 인터페이스
  2. 파일이 생성되지 않은 경우를 대비해 직렬 변환기의 기본값을 정의합니다.
  3. readFrom/writeTo 메소드를 재정의하고 객체가 데이터 유형인지 확인합니다.

데이터 유형과 직렬 변환기가 포함된 .proto 파일이 있으므로 다음 단계는 DataStore를 인스턴스화하는 것입니다. dataStore에서 생성된 속성 위임을 사용하여 이를 수행합니다. 이를 위해서는 데이터가 저장될 파일 이름과 직렬 변환기 클래스(위에서 정의한)를 제공해야 합니다.

private const val DATA_STORE_FILE_NAME = "todo.pb"
private val Context.todoItemDatastore: DataStore<TodoItems> by dataStore(
 fileName = DATA_STORE_FILE_NAME,
 serializer = TodoItemSerializer,
)

이 코드 조각은 클래스 자체 정의보다 선택한 클래스의 최상위에 있어야 합니다. 즉:

private const val DATA_STORE_FILE_NAME = "todo.pb"
private val Context.todoItemDatastore: DataStore<TodoItems> by dataStore(
 fileName = DATA_STORE_FILE_NAME,
 serializer = TodoItemSerializer,
)
class YourClassName {
}

애플리케이션의 나머지 부분에서 이 객체에 액세스하려면 컨텍스트를 사용해야 합니다. 예를 들어 viewmodel 클래스에서 애플리케이션 컨텍스트를 사용하는 것입니다.

class MyViewModel(application: Application): AndroidViewModel(application) {
 val todoDataStore = application.todoItemDataStore
 //...
}

Kotlin Flow 사용 방법

이제 DataStore에 필요한 모든 설정을 완료했으므로 실제로 DataStore와 상호 작용하는 방법에 대해 논의하겠습니다. 우리는 그것으로부터 데이터를 읽고 쓰고 싶어할 것입니다. 하지만 그렇게 할 수 있는 방법은 여러분에게 익숙한 SharedPreferences와는 다릅니다.

위에서 정의한 DataStore에는 DataStore에서 정의한 속성에 대한 흐름을 노출하는 데이터 필드가 있습니다.

🚰 흐름에 익숙하지 않다면 여기에서 시작하는 것이 좋습니다.

val todoItemFlow: Flow<TodoItems> = todoItemDataStore.data
 .catch { exception ->
 if (exception is IOException) {
 emit(TodoItems.getDefaultInstance())
 } else {
 throw exception
 }
 }

위의 코드는 Proto DataStore에서 데이터를 수집하는 흐름을 정의하는 방법을 보여줍니다. 예외가 발생할 경우를 대비해 catch 블록이 추가되었습니다. DataStore를 정의한 클래스에 이 로직을 배치하고 뷰 모델에서 다음과 같이 사용할 수 있습니다.

val todoItemsFlow: LiveData<TodoItems> = todoItemsRepository.todoItemFlow.asLiveData()

Flow를 LiveData로 어떻게 변환했는지 확인하세요. 우리가 이렇게 한 이유는 두 가지입니다:

  1. 흐름을 사용하는 활동/프래그먼트에 관계없이 흐름을 활성 상태로 유지할 수 있습니다.
  2. LiveData는 많은 개발자에게 친숙한 것이므로 이 예를 최대한 접근하기 쉽게 만들고 싶었습니다.

이를 수행하려면 build.gradle 파일에 다음 종속성을 추가해야 합니다:

implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2"

활동/프래그먼트 클래스에서 다음과 같은 실시간 데이터를 관찰할 수 있습니다:

myViewModel.todoItemFlow.observe(LocalLifecycleOwner.current) { todoItems ->
 // Logic to access data from DataStore
 }

DataStore를 사용해야 하는 이유와 시기

모든 검토를 마친 후 이제 방 안의 코끼리에 대해 이야기할 시간입니다. 기존 프로젝트나 다음 프로젝트에서 DataStore(Preferences 또는 Proto)를 사용해야 합니까?

제 생각에는 대답은 여야 합니다. . Google이 SharedPreferences에서 멀어지고 있다는 사실 외에도 DataStore는 데이터의 지속성이 아닌 애플리케이션에 집중할 수 있도록 많은 이점을 제공합니다.

UI 스레드에서 DataStore와 상호 작용하는 것이 안전하며(자동으로 작업을 I/O로 이동하므로) Flow를 사용하고(아직 사용하지 않은 경우) 내부의 모든 이점을 누릴 수 있습니다. SharedPreferences에서 Preferences DataStore로 쉽게 마이그레이션할 수 있는 옵션도 있습니다.

Proto DataStore 대신 Room 사용을 고려하고 있다면 사용 사례에 따라 다릅니다. 저장하거나 유지하려는 데이터의 양이 적고 부분 업데이트가 필요하지 않은 경우 Proto DataStore를 사용하는 것이 좋습니다. 더 큰 데이터 세트가 있거나 복잡할 수 있는 데이터 세트가 있는 경우 대신 Room을 사용하도록 선택해야 합니다.

이 모든 코드가 애플리케이션에서 어떻게 보이는지 보려면 여기에서 확인하세요.

제가 작성한 다른 기사를 읽고 싶으시다면 여기에서 보실 수 있습니다:

읽어주셔서 감사합니다!

참고자료:

  • 프로토콜 버퍼 문서(proto 3)
  • Proto DataStore Codelab 작업
  • 데이터 저장소 문서

무료로 코딩을 배우세요. freeCodeCamp의 오픈 소스 커리큘럼은 40,000명 이상의 사람들이 개발자로 취업하는 데 도움을 주었습니다. 시작하세요