Cấu trúc ứng dụng: Lớp dữ liệu – DataStore – Nhà phát triển Android

Dự án: /architecture/_project.yaml Sách: /architecture/_book.yaml từ khoá: datastore, architecture, api:JetpackDataStore description: Khám phá hướng dẫn về cấu trúc ứng dụng này trên các thư viện lớp dữ liệu để tìm hiểu về Preferences DataStore và Proto DataStore, cách thiết lập và các thông tin khác. hide_page_heading: true

DataStore   Một phần của Android Jetpack.

Dùng thử với Kotlin Multiplatform
Kotlin Multiplatform cho phép chia sẻ lớp dữ liệu với các nền tảng khác. Tìm hiểu cách thiết lập và sử dụng DataStore trong KMP

Jetpack DataStore là một giải pháp lưu trữ dữ liệu cho phép bạn lưu trữ các cặp khoá-giá trị hoặc đối tượng đã nhập có vùng đệm giao thức. DataStore sử dụng coroutine Kotlin và Luồng (Flow) để lưu trữ dữ liệu một cách không đồng bộ, nhất quán và có thể chia sẻ.

Nếu bạn đang sử dụng SharedPreferences để lưu trữ dữ liệu, hãy cân nhắc việc di chuyển sang DataStore.

DataStore API

Giao diện DataStore cung cấp API sau:

  1. Một luồng có thể dùng để đọc dữ liệu từ DataStore

    val data: Flow<T>
    
  2. Một hàm để cập nhật dữ liệu trong DataStore

    suspend updateData(transform: suspend (t) -> T)
    

Cấu hình DataStore

Nếu muốn lưu trữ và truy cập vào dữ liệu bằng các khoá, hãy sử dụng phương thức triển khai Preferences DataStore. Phương thức này không yêu cầu có giản đồ được xác định trước và không đảm bảo an toàn về kiểu. Nó có một API tương tự như SharedPreferences nhưng không có những nhược điểm liên quan đến các lựa chọn ưu tiên dùng chung.

DataStore cho phép bạn duy trì các lớp tuỳ chỉnh. Để làm việc này, bạn phải xác định một giản đồ cho dữ liệu và cung cấp một Serializer để chuyển đổi dữ liệu đó thành một định dạng có thể duy trì. Bạn có thể chọn sử dụng Protocol Buffers, JSON hoặc bất kỳ chiến lược chuyển đổi tuần tự nào khác.

Thiết lập

Để sử dụng Jetpack DataStore trong ứng dụng, hãy thêm đoạn mã sau vào tệp Gradle tuỳ thuộc vào phương thức triển khai mà bạn muốn dùng:

Preferences DataStore

Thêm các dòng sau vào phần phụ thuộc của tệp gradle:

Groovy

    dependencies {
        // Preferences DataStore (SharedPreferences like APIs)
        implementation "androidx.datastore:datastore-preferences:1.1.7"

        // Alternatively - without an Android dependency.
        implementation "androidx.datastore:datastore-preferences-core:1.1.7"
    }
    

Kotlin

    dependencies {
        // Preferences DataStore (SharedPreferences like APIs)
        implementation("androidx.datastore:datastore-preferences:1.1.7")

        // Alternatively - without an Android dependency.
        implementation("androidx.datastore:datastore-preferences-core:1.1.7")
    }
    

Để thêm tính năng hỗ trợ RxJava (không bắt buộc), hãy thêm các phần phụ thuộc sau:

Groovy

    dependencies {
        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-preferences-rxjava2:1.1.7"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-preferences-rxjava3:1.1.7"
    }
    

Kotlin

    dependencies {
        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-preferences-rxjava2:1.1.7")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7")
    }
    

DataStore

Thêm các dòng sau vào phần phụ thuộc của tệp gradle:

Groovy

    dependencies {
        // Typed DataStore for custom data objects (for example, using Proto or JSON).
        implementation "androidx.datastore:datastore:1.1.7"

        // Alternatively - without an Android dependency.
        implementation "androidx.datastore:datastore-core:1.1.7"
    }
    

Kotlin

    dependencies {
        // Typed DataStore for custom data objects (for example, using Proto or JSON).
        implementation("androidx.datastore:datastore:1.1.7")

        // Alternatively - without an Android dependency.
        implementation("androidx.datastore:datastore-core:1.1.7")
    }
    

Thêm các phần phụ thuộc không bắt buộc sau đây để hỗ trợ RxJava:

Groovy

    dependencies {
        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-rxjava2:1.1.7"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-rxjava3:1.1.7"
    }
    

Kotlin

    dependencies {
        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-rxjava2:1.1.7")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-rxjava3:1.1.7")
    }
    

Để chuyển đổi tuần tự nội dung, hãy thêm các phần phụ thuộc cho tính năng chuyển đổi tuần tự JSON hoặc vùng đệm giao thức.

Chuyển đổi tuần tự JSON

Để sử dụng tính năng chuyển đổi tuần tự JSON, hãy thêm nội dung sau vào tệp Gradle của bạn:

Groovy

    plugins {
        id("org.jetbrains.kotlin.plugin.serialization") version "2.2.20"
    }

    dependencies {
        implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0"
    }
    

Kotlin

    plugins {
        id("org.jetbrains.kotlin.plugin.serialization") version "2.2.20"
    }

    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
    }
    

Chuyển đổi tuần tự Protobuf

Để sử dụng quy trình chuyển đổi tuần tự Protobuf, hãy thêm nội dung sau vào tệp Gradle của bạn:

Groovy

    plugins {
        id("com.google.protobuf") version "0.9.5"
    }
    dependencies {
        implementation "com.google.protobuf:protobuf-kotlin-lite:4.32.1"

    }

    protobuf {
        protoc {
            artifact = "com.google.protobuf:protoc:4.32.1"
        }
        generateProtoTasks {
            all().forEach { task ->
                task.builtins {
                    create("java") {
                        option("lite")
                    }
                    create("kotlin")
                }
            }
        }
    }
    

Kotlin

    plugins {
        id("com.google.protobuf") version "0.9.5"
    }
    dependencies {
        implementation("com.google.protobuf:protobuf-kotlin-lite:4.32.1")
    }

    protobuf {
        protoc {
            artifact = "com.google.protobuf:protoc:4.32.1"
        }
        generateProtoTasks {
            all().forEach { task ->
                task.builtins {
                    create("java") {
                        option("lite")
                    }
                    create("kotlin")
                }
            }
        }
    }
    

Sử dụng DataStore đúng cách

Để sử dụng DataStore đúng cách, hãy luôn ghi nhớ các quy tắc sau:

  1. Tuyệt đối không tạo nhiều thực thể của DataStore cho một tệp nhất định trong cùng một quy trình. Làm như vậy có thể phá vỡ tất cả chức năng của DataStore. Nếu có nhiều DataStore đang hoạt động cho một tệp nhất định trong cùng một quy trình, thì DataStore sẽ gửi IllegalStateException khi đọc hoặc cập nhật dữ liệu.

  2. Không được thay đổi loại chung của DataStore<T>. Thay đổi một kiểu dữ liệu dùng trong DataStore sẽ vô hiệu hoá tính nhất quán mà DataStore cung cấp và có thể tạo ra các lỗi nghiêm trọng và khó phát hiện. Bạn nên sử dụng vùng đệm giao thức để đảm bảo tính bất biến, API rõ ràng và quá trình chuyển đổi tuần tự hiệu quả.

  3. Tuyệt đối không sử dụng kết hợp SingleProcessDataStoreMultiProcessDataStore cho cùng một tệp. Nếu có ý định truy cập vào DataStore từ nhiều quy trình, bạn phải sử dụng MultiProcessDataStore.

Định nghĩa dữ liệu

Preferences DataStore

Xác định một khoá sẽ được dùng để duy trì dữ liệu trên ổ đĩa.

val EXAMPLE_COUNTER = intPreferencesKey("example_counter")

DataStore JSON

Đối với kho dữ liệu JSON, hãy thêm chú thích @Serialization vào dữ liệu mà bạn muốn duy trì

@Serializable
data class Settings(
    val exampleCounter: Int
)

Xác định một lớp triển khai Serializer<T>, trong đó T là loại của lớp mà bạn đã thêm chú thích trước đó. Hãy đảm bảo rằng bạn cung cấp giá trị mặc định để trình chuyển đổi tuần tự sử dụng nếu chưa có tệp nào được tạo.

object SettingsSerializer : Serializer<Settings> {

    override val defaultValue: Settings = Settings(exampleCounter = 0)

    override suspend fun readFrom(input: InputStream): Settings =
        try {
            Json.decodeFromString<Settings>(
                input.readBytes().decodeToString()
            )
        } catch (serialization: SerializationException) {
            throw CorruptionException("Unable to read Settings", serialization)
        }

    override suspend fun writeTo(t: Settings, output: OutputStream) {
        output.write(
            Json.encodeToString(t)
                .encodeToByteArray()
        )
    }
}

Proto DataStore

Phương thức triển khai DataStore Proto sử dụng DataStore và các vùng đệm giao thức để lưu trữ các đối tượng đã nhập vào ổ đĩa.

DataStore Proto yêu cầu một giản đồ được xác định trước trong tệp proto trong thư mục app/src/main/proto/. Giản đồ này xác định loại cho các đối tượng mà bạn lưu trữ trong DataStore Proto. Để tìm hiểu thêm về cách xác định một giản đồ proto, hãy xem hướng dẫn về ngôn ngữ protobuf.

Thêm một tệp có tên là settings.proto vào thư mục src/main/proto:

syntax = "proto3";

option java_package = "com.example.datastore.snippets.proto";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

Xác định một lớp triển khai Serializer<T>, trong đó T là loại được xác định trong tệp proto. Lớp chuyển đổi tuần tự này xác định cách DataStore đọc và ghi loại dữ liệu của bạn. Hãy đảm bảo rằng bạn cung cấp giá trị mặc định để trình chuyển đổi tuần tự sử dụng nếu chưa có tệp nào được tạo.

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) {
        return t.writeTo(output)
    }
}

Tạo DataStore

Bạn cần chỉ định tên cho tệp dùng để duy trì dữ liệu.

Preferences DataStore

Phương thức triển khai Preferences DataStore sử dụng các lớp DataStorePreferences để lưu trữ các cặp khoá-giá trị vào ổ đĩa. Hãy dùng phương thức uỷ quyền thuộc tính do preferencesDataStore tạo để tạo một thực thể DataStore<Preferences>. Gọi tệp này một lần ở cấp cao nhất của tệp Kotlin. Truy cập DataStore thông qua thuộc tính này trong suốt phần còn lại của ứng dụng. Thao tác này giúp bạn dễ dàng giữ DataStore ở dạng một singleton. Ngoài ra, hãy dùng RxPreferenceDataStoreBuilder nếu bạn đang dùng RxJava. Tham số bắt buộc name là tên của Preferences DataStore.

// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

DataStore JSON

Sử dụng tính năng uỷ quyền thuộc tính do dataStore tạo để tạo một thực thể của DataStore<T>, trong đó T là lớp dữ liệu có thể chuyển đổi tuần tự. Hãy gọi tệp kotlin của bạn một lần ở cấp cao nhất và truy cập tệp thông qua tính năng uỷ quyền thuộc tính này trong suốt thời gian còn lại của ứng dụng. Tham số fileName cho DataStore biết nên sử dụng tệp nào để lưu trữ dữ liệu, còn tham số serializer cho DataStore biết tên của lớp chuyển đổi tuần tự đã xác định trong bước 1.

val Context.dataStore: DataStore<Settings> by dataStore(
    fileName = "settings.json",
    serializer = SettingsSerializer,
)

Proto DataStore

Sử dụng tính năng uỷ quyền thuộc tính do dataStore tạo để tạo một bản sao của DataStore<T>, trong đó T là loại được xác định trong tệp proto. Hãy gọi tệp kotlin của bạn một lần ở cấp cao nhất và truy cập tệp thông qua tính năng uỷ quyền thuộc tính này trong suốt thời gian còn lại của ứng dụng. Tham số fileName cho DataStore biết nên sử dụng tệp nào để lưu trữ dữ liệu, còn tham số serializer cho DataStore biết tên của lớp chuyển đổi tuần tự đã xác định trong bước 1.

val Context.dataStore: DataStore<Settings> by dataStore(
    fileName = "settings.pb",
    serializer = SettingsSerializer,
)

Đọc từ DataStore

Bạn cần chỉ định tên cho tệp dùng để duy trì dữ liệu.

Preferences DataStore

Do Preferences DataStore không sử dụng giản đồ được xác định trước, bạn phải sử dụng hàm loại khoá tương ứng để xác định khoá cho mỗi giá trị mà bạn cần lưu trữ trong phiên bản DataStore<Preferences>. Ví dụ: để xác định khoá cho một giá trị int, hãy sử dụng intPreferencesKey(). Sau đó, hãy dùng thuộc tính DataStore.data để hiển thị giá trị được lưu trữ phù hợp bằng một Flow.

fun counterFlow(): Flow<Int> = context.dataStore.data.map { preferences ->
    preferences[EXAMPLE_COUNTER] ?: 0
}

DataStore JSON

Hãy sử dụng DataStore.data để hiển thị Flow của thuộc tính phù hợp trong đối tượng được lưu trữ của bạn.

fun counterFlow(): Flow<Int> = context.dataStore.data.map { settings ->
    settings.exampleCounter
}

Proto DataStore

Hãy sử dụng DataStore.data để hiển thị Flow của thuộc tính phù hợp trong đối tượng được lưu trữ của bạn.

fun counterFlow(): Flow<Int> = context.dataStore.data.map { settings ->
    settings.exampleCounter
}

Ghi vào DataStore

DataStore cung cấp một hàm updateData() có thể chia sẻ cập nhật một đối tượng được lưu trữ. updateData cho bạn biết trạng thái hiện tại của dữ liệu làm một phiên bản cho loại dữ liệu và cập nhật dữ liệu có thể chia sẻ trong thao tác đọc-ghi-sửa đổi nguyên tử. Tất cả mã trong khối updateData đều được coi là một giao dịch duy nhất.

Preferences DataStore

suspend fun incrementCounter() {
    context.dataStore.updateData {
        it.toMutablePreferences().also { preferences ->
            preferences[EXAMPLE_COUNTER] = (preferences[EXAMPLE_COUNTER] ?: 0) + 1
        }
    }
}

DataStore JSON

suspend fun incrementCounter() {
    context.dataStore.updateData { settings ->
        settings.copy(exampleCounter = settings.exampleCounter + 1)
    }
}

Proto DataStore

suspend fun incrementCounter() {
    context.dataStore.updateData { settings ->
        settings.copy { exampleCounter = exampleCounter + 1 }
    }
}

Ứng dụng mẫu được tạo bằng Compose

Bạn có thể đặt các hàm này vào một lớp và sử dụng trong ứng dụng Compose.

Preferences DataStore

Giờ đây, chúng ta có thể đặt các hàm này vào một lớp có tên là PreferencesDataStore và sử dụng lớp này trong một Ứng dụng Compose.

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val preferencesDataStore = remember(context) { PreferencesDataStore(context) }

// Display counter value.
val exampleCounter by preferencesDataStore.counterFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Counter $exampleCounter",
    fontSize = 25.sp
)

// Update the counter.
Button(
    onClick = {
        coroutineScope.launch { preferencesDataStore.incrementCounter() }
    }
) {
    Text("increment")
}

DataStore JSON

Giờ đây, chúng ta có thể đặt các hàm này vào một lớp có tên là JSONDataStore và sử dụng lớp này trong một ứng dụng Compose.

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val jsonDataStore = remember(context) { JsonDataStore(context) }

// Display counter value.
val exampleCounter by jsonDataStore.counterFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Counter $exampleCounter",
    fontSize = 25.sp
)

// Update the counter.
Button(onClick = { coroutineScope.launch { jsonDataStore.incrementCounter() } }) {
    Text("increment")
}

Proto DataStore

Giờ đây, chúng ta có thể đặt các hàm này vào một lớp có tên là ProtoDataStore và sử dụng lớp này trong một ứng dụng Compose.

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val protoDataStore = remember(context) { ProtoDataStore(context) }

// Display counter value.
val exampleCounter by protoDataStore.counterFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Counter $exampleCounter",
    fontSize = 25.sp
)

// Update the counter.
Button(onClick = { coroutineScope.launch { protoDataStore.incrementCounter() } }) {
    Text("increment")
}

Sử dụng DataStore trong mã đồng bộ

Một trong những lợi ích chính của DataStore là API không đồng bộ, nhưng không phải lúc nào bạn cũng có thể thay đổi mã xung quanh thành không đồng bộ. Điều này có thể xảy ra trong trường hợp bạn đang xử lý một cơ sở mã hiện có sử dụng ổ đĩa I/O đồng bộ hoặc nếu bạn có một phần phụ thuộc không cung cấp API không đồng bộ.

Coroutine của Kotlin cung cấp trình tạo coroutine runBlocking() để giúp thu hẹp khoảng cách giữa mã đồng bộ và không đồng bộ. Bạn có thể sử dụng runBlocking() để đọc dữ liệu trong DataStore một cách đồng bộ. RxJava cung cấp các phương thức chặn trên Flowable. Mã sau đây chặn luồng gọi cho đến khi DataStore trả về dữ liệu:

Kotlin

val exampleData = runBlocking { context.dataStore.data.first() }

Java

Settings settings = dataStore.data().blockingFirst();

Việc thực hiện các thao tác I/O đồng bộ trên luồng giao diện người dùng có thể khiến ANR hoặc giao diện người dùng không phản hồi. Bạn có thể giảm thiểu những vấn đề này bằng cách tải trước không đồng bộ dữ liệu trong DataStore:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        context.dataStore.data.first()
        // You should also handle IOExceptions here.
    }
}

Java

dataStore.data().first().subscribe();

Bằng cách này, DataStore đọc không đồng bộ dữ liệu và lưu dữ liệu vào bộ nhớ đệm trong bộ nhớ. Các lượt đọc đồng bộ sau này sử dụng runBlocking() có thể nhanh hơn hoặc có thể tránh được toàn bộ thao tác của ổ đĩa I/O nếu lần đọc ban đầu hoàn tất.

Sử dụng DataStore trong mã đa quá trình

Bạn có thể định cấu hình DataStore để truy cập vào cùng một dữ liệu trong nhiều quy trình với các thuộc tính nhất quán của dữ liệu như trong một quy trình. Cụ thể, DataStore cung cấp:

  • Hoạt động đọc chỉ trả về dữ liệu đã lưu trữ trên ổ đĩa.
  • Tính nhất quán đọc sau khi ghi.
  • Hoạt động ghi được chuyển đổi tuần tự.
  • Hoạt động đọc tuyệt đối không bị chặn bởi hoạt động ghi.

Hãy xem xét một ứng dụng mẫu có dịch vụ và hoạt động, trong đó dịch vụ đang chạy trong một quy trình riêng và định kỳ cập nhật DataStore.

Ví dụ này sử dụng một kho dữ liệu JSON, nhưng bạn cũng có thể sử dụng một kho dữ liệu proto hoặc các lựa chọn ưu tiên.

@Serializable
data class Time(
    val lastUpdateMillis: Long
)

Trình chuyển đổi tuần tự cho DataStore biết cách đọc và ghi loại dữ liệu của bạn. Hãy đảm bảo rằng bạn cung cấp giá trị mặc định để trình chuyển đổi tuần tự sử dụng nếu chưa có tệp nào được tạo. Sau đây là một ví dụ về phương thức triển khai bằng kotlinx.serialization:

object TimeSerializer : Serializer<Time> {

    override val defaultValue: Time = Time(lastUpdateMillis = 0L)

    override suspend fun readFrom(input: InputStream): Time =
        try {
            Json.decodeFromString<Time>(
                input.readBytes().decodeToString()
            )
        } catch (serialization: SerializationException) {
            throw CorruptionException("Unable to read Time", serialization)
        }

    override suspend fun writeTo(t: Time, output: OutputStream) {
        output.write(
            Json.encodeToString(t)
                .encodeToByteArray()
        )
    }
}

Để có thể sử dụng DataStore trong nhiều quy trình, bạn cần tạo đối tượng DataStore bằng MultiProcessDataStoreFactory cho cả ứng dụng và mã dịch vụ:

val dataStore = MultiProcessDataStoreFactory.create(
    serializer = TimeSerializer,
    produceFile = {
        File("${context.cacheDir.path}/time.pb")
    },
    corruptionHandler = null
)

Thêm nội dung sau vào AndroidManifiest.xml:

<service
    android:name=".TimestampUpdateService"
    android:process=":my_process_id" />

Dịch vụ này định kỳ gọi updateLastUpdateTime(), ghi vào kho dữ liệu bằng updateData.

suspend fun updateLastUpdateTime() {
    dataStore.updateData { time ->
        time.copy(lastUpdateMillis = System.currentTimeMillis())
    }
}

Ứng dụng đọc giá trị do dịch vụ ghi bằng luồng dữ liệu:

fun timeFlow(): Flow<Long> = dataStore.data.map { time ->
    time.lastUpdateMillis
}

Giờ đây, chúng ta có thể đặt tất cả các hàm này vào một lớp có tên là MultiProcessDataStore và sử dụng lớp này trong một Ứng dụng.

Sau đây là mã dịch vụ:

class TimestampUpdateService : Service() {
    val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    val multiProcessDataStore by lazy { MultiProcessDataStore(applicationContext) }


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        serviceScope.launch {
            while (true) {
                multiProcessDataStore.updateLastUpdateTime()
                delay(1000)
            }
        }
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        serviceScope.cancel()
    }
}

Và mã ứng dụng:

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val multiProcessDataStore = remember(context) { MultiProcessDataStore(context) }

// Display time written by other process.
val lastUpdateTime by multiProcessDataStore.timeFlow()
    .collectAsState(initial = 0, coroutineScope.coroutineContext)
Text(
    text = "Last updated: $lastUpdateTime",
    fontSize = 25.sp
)

DisposableEffect(context) {
    val serviceIntent = Intent(context, TimestampUpdateService::class.java)
    context.startService(serviceIntent)
    onDispose {
        context.stopService(serviceIntent)
    }
}

Bạn có thể sử dụng tính năng chèn phần phụ thuộc Hilt để đảm bảo thực thể DataStore là duy nhất cho mỗi quy trình:

@Provides
@Singleton
fun provideDataStore(@ApplicationContext context: Context): DataStore<Settings> =
   MultiProcessDataStoreFactory.create(...)

Xử lý tình trạng hỏng tệp

Trong một số ít trường hợp, tệp liên tục trên đĩa của DataStore có thể bị hỏng. Theo mặc định, DataStore không tự động khôi phục dữ liệu bị hỏng và các nỗ lực đọc dữ liệu từ DataStore sẽ khiến hệ thống gửi một CorruptionException.

DataStore cung cấp một API trình xử lý hỏng có thể giúp bạn khôi phục một cách suôn sẻ trong trường hợp như vậy và tránh việc đưa ra ngoại lệ. Khi được định cấu hình, trình xử lý lỗi hỏng sẽ thay thế tệp bị hỏng bằng một tệp mới chứa giá trị mặc định được xác định trước.

Để thiết lập trình xử lý này, hãy cung cấp một corruptionHandler khi tạo thực thể DataStore trong by dataStore() hoặc trong phương thức của nhà máy DataStoreFactory:

val dataStore: DataStore<Settings> = DataStoreFactory.create(
   serializer = SettingsSerializer(),
   produceFile = {
       File("${context.cacheDir.path}/myapp.preferences_pb")
   },
   corruptionHandler = ReplaceFileCorruptionHandler { Settings(lastUpdate = 0) }
)

Gửi ý kiến phản hồi

Hãy chia sẻ phản hồi và ý kiến của bạn với chúng tôi thông qua các tài nguyên sau:

Công cụ theo dõi lỗi:
Báo cáo sự cố để chúng tôi có thể sửa lỗi.

Tài nguyên khác

Để tìm hiểu thêm về Jetpack DataStore, hãy xem thêm các tài nguyên sau:

Mẫu

Blog

Lớp học lập trình