DataStore   ซึ่งเป็นส่วนหนึ่งของ Android Jetpack.

ลองใช้กับ Kotlin Multiplatform
Kotlin Multiplatform ช่วยให้แชร์ชั้นข้อมูลกับแพลตฟอร์มอื่นๆ ได้ ดูวิธีตั้งค่าและทำงานกับ DataStore ใน KMP

Jetpack DataStore เป็นโซลูชันพื้นที่เก็บข้อมูลที่ช่วยให้คุณจัดเก็บคู่คีย์-ค่า หรือออบเจ็กต์ที่มีการพิมพ์ด้วย บัฟเฟอร์โปรโตคอลได้ DataStore ใช้โครูทีนและ Flow ของ Kotlin เพื่อจัดเก็บข้อมูลแบบไม่พร้อมกัน อย่างสม่ำเสมอ และเป็นธุรกรรม

หากใช้ SharedPreferences เพื่อจัดเก็บข้อมูล ให้พิจารณาย้ายข้อมูลไปยัง DataStore แทน

DataStore API

อินเทอร์เฟซ DataStore มี API ดังนี้

  1. Flow ที่ใช้เพื่ออ่านข้อมูลจาก DataStore ได้

    val data: Flow<T>
    
  2. ฟังก์ชันสำหรับอัปเดตข้อมูลใน DataStore

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

การกำหนดค่า DataStore

หากต้องการจัดเก็บและเข้าถึงข้อมูลโดยใช้คีย์ ให้ใช้การติดตั้งใช้งาน Preferences DataStore ซึ่งไม่จำเป็นต้องมีสคีมาที่กำหนดไว้ล่วงหน้า และไม่มีความปลอดภัยในการกำหนดประเภท โดยมี API ที่คล้ายกับ SharedPreferences แต่ไม่มี ข้อเสียที่เกี่ยวข้องกับค่ากำหนดที่แชร์

DataStore ช่วยให้คุณเก็บคลาสที่กำหนดเองไว้ได้ หากต้องการทำเช่นนี้ คุณต้องกำหนดสคีมาสำหรับข้อมูลและระบุ Serializer เพื่อแปลงข้อมูลให้อยู่ในรูปแบบที่เก็บไว้ได้ คุณสามารถเลือกใช้บัฟเฟอร์โปรโตคอล, JSON หรือกลยุทธ์การซีเรียลไลซ์อื่นๆ

ตั้งค่า

หากต้องการใช้ Jetpack DataStore ในแอป ให้เพิ่มข้อมูลต่อไปนี้ลงในไฟล์ Gradle โดยขึ้นอยู่กับการติดตั้งใช้งานที่ต้องการใช้

Preferences DataStore

เพิ่มบรรทัดต่อไปนี้ลงในส่วนทรัพยากร Dependency ของไฟล์ Gradle

Groovy

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

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

Kotlin

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

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

หากต้องการเพิ่มการรองรับ RxJava (ไม่บังคับ) ให้เพิ่มทรัพยากร Dependency ต่อไปนี้

Groovy

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

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

Kotlin

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

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

DataStore

เพิ่มบรรทัดต่อไปนี้ลงในส่วนทรัพยากร Dependency ของไฟล์ Gradle

Groovy

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

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

Kotlin

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

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

เพิ่มทรัพยากร Dependency ที่ไม่บังคับต่อไปนี้สำหรับการรองรับ RxJava

Groovy

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

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

Kotlin

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

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

หากต้องการทำให้เนื้อหาเป็นแบบอนุกรม ให้เพิ่มการอ้างอิงสำหรับการทำให้เป็นอนุกรมของ Protocol Buffers หรือ JSON

การซีเรียลไลซ์ JSON

หากต้องการใช้การซีเรียลไลซ์ JSON ให้เพิ่มค่าต่อไปนี้ลงในไฟล์ Gradle

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")
    }
    

การทำให้เป็นอนุกรม Protobuf

หากต้องการใช้การซีเรียลไลซ์ Protobuf ให้เพิ่มข้อมูลต่อไปนี้ลงในไฟล์ Gradle

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")
                }
            }
        }
    }
    

ใช้ DataStore อย่างถูกต้อง

โปรดคำนึงถึงกฎต่อไปนี้เสมอเพื่อให้ใช้ DataStore ได้อย่างถูกต้อง

  1. อย่าสร้างอินสแตนซ์ของ DataStore มากกว่า 1 รายการสำหรับไฟล์ที่กำหนดใน กระบวนการเดียวกัน การทำเช่นนี้อาจทำให้ฟังก์ชันการทำงานทั้งหมดของ DataStore หยุดทำงาน หากมี DataStore หลายรายการที่ใช้งานอยู่สำหรับไฟล์ที่กำหนดในกระบวนการเดียวกัน DataStore จะแสดง IllegalStateException เมื่ออ่านหรืออัปเดตข้อมูล

  2. ประเภททั่วไปของ DataStore<T> ต้องเปลี่ยนแปลงไม่ได้ การเปลี่ยนแปลงประเภทที่ใช้ใน DataStore จะทำให้ความสอดคล้องที่ DataStore มีให้ไม่ถูกต้อง และอาจทำให้เกิดข้อบกพร่องที่ร้ายแรงและตรวจพบได้ยาก เราขอแนะนำให้คุณใช้บัฟเฟอร์โปรโตคอล ซึ่งช่วยให้มั่นใจได้ว่าข้อมูลจะเปลี่ยนแปลงไม่ได้ มี API ที่ชัดเจน และการซีเรียลไลซ์มีประสิทธิภาพ

  3. อย่าใช้ SingleProcessDataStore และ MultiProcessDataStore ร่วมกันสำหรับไฟล์เดียวกัน หากต้องการเข้าถึง DataStore จากกระบวนการมากกว่า 1 รายการ คุณต้องใช้ MultiProcessDataStore

คำจำกัดความของข้อมูล

Preferences DataStore

กำหนดคีย์ที่จะใช้เพื่อเก็บข้อมูลไว้ในดิสก์

val EXAMPLE_COUNTER = intPreferencesKey("example_counter")

JSON DataStore

สำหรับ JSON DataStore ให้เพิ่มคำอธิบายประกอบ @Serialization ลงในข้อมูลที่ต้องการเก็บไว้

@Serializable
data class Settings(
    val exampleCounter: Int
)

กำหนดคลาสที่ใช้ Serializer<T> โดยที่ T คือประเภทของ คลาสที่คุณเพิ่มคำอธิบายประกอบไว้ก่อนหน้านี้ ตรวจสอบว่าคุณได้ใส่ค่าเริ่มต้นสำหรับ Serializer ที่จะใช้หากยังไม่มีการสร้างไฟล์

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

การติดตั้งใช้งาน Proto DataStore ใช้ DataStore และ บัฟเฟอร์โปรโตคอล เพื่อ เก็บออบเจ็กต์ที่มีการพิมพ์ไว้ในดิสก์

Proto DataStore ต้องมีสคีมาที่กำหนดไว้ล่วงหน้าในไฟล์โปรโตในไดเรกทอรี app/src/main/proto/ สคีมานี้กำหนดประเภทสำหรับออบเจ็กต์ที่คุณเก็บไว้ใน Proto DataStore ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดสคีมาโปรโต ได้ที่คู่มือภาษาโปรโตบัฟ

เพิ่มไฟล์ที่ชื่อว่า settings.proto ลงในโฟลเดอร์ src/main/proto ดังนี้

syntax = "proto3";

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

message Settings {
  int32 example_counter = 1;
}

กำหนดคลาสที่ใช้ Serializer<T> โดยที่ T คือประเภทที่กำหนดไว้ ในไฟล์โปรโต คลาส Serializer นี้กำหนดวิธีที่ DataStore อ่านและเขียนประเภทข้อมูลของคุณ ตรวจสอบว่าคุณได้ใส่ค่าเริ่มต้นสำหรับ Serializer ที่จะใช้หากยังไม่มีการสร้างไฟล์

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)
    }
}

สร้าง DataStore

คุณต้องระบุชื่อสำหรับไฟล์ที่จะใช้เพื่อเก็บข้อมูลไว้

Preferences DataStore

การติดตั้งใช้งาน Preferences DataStore ใช้คลาส DataStore และ Preferences เพื่อเก็บคู่คีย์-ค่าไว้ในดิสก์ ใช้ พร็อพเพอร์ตี้ Delegate ที่สร้างโดย preferencesDataStore เพื่อสร้างอินสแตนซ์ ของ DataStore<Preferences> เรียกใช้พร็อพเพอร์ตี้นี้ 1 ครั้งที่ระดับบนสุดของไฟล์ Kotlin เข้าถึง DataStore ผ่านพร็อพเพอร์ตี้นี้ในส่วนอื่นๆ ของแอปพลิเคชัน ซึ่งจะช่วยให้คุณเก็บ DataStore ไว้เป็น Singleton ได้ง่ายขึ้น พารามิเตอร์ name ที่จำเป็นคือชื่อของ Preferences DataStore

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

JSON DataStore

ใช้พร็อพเพอร์ตี้ Delegate ที่สร้างโดย dataStore เพื่อสร้างอินสแตนซ์ของ DataStore<T> โดยที่ T คือคลาสข้อมูลที่ซีเรียลไลซ์ได้ เรียกใช้พร็อพเพอร์ตี้นี้ 1 ครั้งที่ระดับบนสุดของไฟล์ Kotlin และเข้าถึงพร็อพเพอร์ตี้นี้ผ่านพร็อพเพอร์ตี้ Delegate ในส่วนอื่นๆ ของแอป พารามิเตอร์ fileName จะบอก DataStore ว่าจะใช้ไฟล์ใดในการจัดเก็บข้อมูล และพารามิเตอร์ serializer จะบอก DataStore ว่าจะใช้คลาส Serializer ที่กำหนดไว้ก่อนหน้านี้

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

Proto DataStore

ใช้พร็อพเพอร์ตี้ Delegate ที่สร้างโดย dataStore เพื่อสร้างอินสแตนซ์ของ DataStore<T> โดยที่ T คือประเภทที่กำหนดไว้ในไฟล์โปรโต เรียกใช้พร็อพเพอร์ตี้นี้ 1 ครั้งที่ระดับบนสุดของไฟล์ Kotlin และเข้าถึงพร็อพเพอร์ตี้นี้ผ่านพร็อพเพอร์ตี้ Delegate ในส่วนอื่นๆ ของแอป พารามิเตอร์ fileName จะบอก DataStore ว่าจะใช้ไฟล์ใดในการจัดเก็บข้อมูล และพารามิเตอร์ serializer จะบอก DataStore ว่าจะใช้คลาส Serializer ที่กำหนดไว้ก่อนหน้านี้

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

อ่านจาก DataStore

คุณต้องระบุชื่อสำหรับไฟล์ที่จะใช้เพื่อเก็บข้อมูลไว้

Preferences DataStore

เนื่องจาก Preferences DataStore ไม่ได้ใช้สคีมาที่กำหนดไว้ล่วงหน้า คุณจึงต้องใช้ ฟังก์ชันประเภทคีย์ที่เกี่ยวข้องเพื่อกำหนดคีย์สำหรับแต่ละค่าที่คุณ ต้องการจัดเก็บในอินสแตนซ์ DataStore<Preferences> เช่น หากต้องการกำหนด คีย์สำหรับค่าจำนวนเต็ม ให้ใช้ intPreferencesKey จากนั้นใช้พร็อพเพอร์ตี้ DataStore.data เพื่อแสดงค่าที่จัดเก็บไว้ที่เหมาะสมโดยใช้ Flow

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

JSON DataStore

ใช้ DataStore.data เพื่อแสดง Flow ของพร็อพเพอร์ตี้ที่เหมาะสมจากออบเจ็กต์ที่จัดเก็บไว้

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

Proto DataStore

ใช้ DataStore.data เพื่อแสดง Flow ของพร็อพเพอร์ตี้ที่เหมาะสมจากออบเจ็กต์ที่จัดเก็บไว้

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

ใช้ collectAsStateWithLifecycle เพื่อใช้ Flow ที่สร้างโดย ViewModel ใน Composable ซึ่งจะแปลง DataStore Flow เป็น Compose State ที่ทริกเกอร์การคอมโพสใหม่ได้อย่างปลอดภัย

@Composable
fun SomeScreen(counterFlow: Flow<Int>) {
  val counter by counterFlow.collectAsStateWithLifecycle(initialValue = 0)
  Text(text = "Example counter: ${counter}")
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับ collectAsStateWithLifecycle, ได้ที่ State และ Jetpack Compose

เขียนลงใน DataStore

DataStore มีฟังก์ชัน updateData ที่อัปเดตออบเจ็กต์ที่ จัดเก็บไว้แบบเป็นธุรกรรม updateData จะแสดงสถานะปัจจุบันของข้อมูลเป็นอินสแตนซ์ของประเภทข้อมูล และอัปเดตข้อมูลแบบเป็นธุรกรรมในการดำเนินการอ่าน-เขียน-แก้ไขแบบอะตอมมิก ระบบจะถือว่าโค้ดทั้งหมดในบล็อก updateData เป็นธุรกรรมเดียว

Preferences DataStore

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

JSON DataStore

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 }
    }
}

ใช้ DataStore ในแอป Compose

หากต้องการใช้ DataStore ในแอป Compose ให้ทำตามหลักเกณฑ์สถาปัตยกรรมของแอป Android โดยเก็บการดำเนินการ DataStore ไว้ในชั้นข้อมูล (เช่น ที่เก็บ) และแสดงข้อมูลไปยัง UI ผ่าน ViewModel

หลีกเลี่ยงการอ่านหรือเขียนลงใน DataStore โดยตรงภายในฟังก์ชัน Composable

  1. แสดง DataStore ผ่าน ViewModel ส่งที่เก็บ (ซึ่งครอบคลุม DataStore) ไปยัง ViewModel และ แปลง Flow เป็น StateFlow เพื่อให้ UI สังเกตได้ง่าย ดังที่แสดงใน ข้อมูลโค้ดต่อไปนี้

    class SettingsViewModel(
        private val userPreferencesRepository: UserPreferencesRepository
    ) : ViewModel() {
    
        // Expose the DataStore flow as a StateFlow for Compose
        val userSettings: StateFlow<UserSettings> = userPreferencesRepository.userSettingsFlow
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = UserSettings.getDefaultInstance()
            )
    
        fun updateCounter(newValue: Int) {
            viewModelScope.launch {
                userPreferencesRepository.updateCounter(newValue)
            }
        }
    }
    
  2. สังเกตและเขียนจาก Composable ใช้ collectAsStateWithLifecycle เพื่อสังเกต StateFlow ใน UI อย่างปลอดภัย และเรียกใช้ฟังก์ชัน ViewModel เพื่อจัดการการเขียน ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

    @Composable
    fun SettingsScreen(
        viewModel: SettingsViewModel = viewModel()
    ) {
        // Safely collect the state
        val settings by viewModel.userSettings.collectAsStateWithLifecycle()
    
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = "Current counter: ${settings.counter}")
    
            Spacer(modifier = Modifier.height(8.dp))
    
            Button(onClick = { viewModel.updateCounter(settings.counter + 1) }) {
                Text("Increment Counter")
            }
        }
    }
    

ใช้ DataStore ในโค้ดแบบหลายกระบวนการ

คุณสามารถกำหนดค่า DataStore เพื่อเข้าถึงข้อมูลเดียวกันในกระบวนการต่างๆ ด้วยพร็อพเพอร์ตี้ความสอดคล้องของข้อมูลเดียวกันกับที่อยู่ในกระบวนการเดียว โดยเฉพาะอย่างยิ่ง DataStore มีพร็อพเพอร์ตี้ต่อไปนี้

  • การอ่านจะแสดงเฉพาะข้อมูลที่เก็บไว้ในดิสก์เท่านั้น
  • ความสอดคล้องแบบอ่านหลังเขียน
  • การเขียนจะถูกซีเรียลไลซ์
  • การอ่านจะไม่ถูกบล็อกโดยการเขียน

ลองดูแอปพลิเคชันตัวอย่างที่มีบริการและกิจกรรมที่บริการทำงานในกระบวนการแยกต่างหากและอัปเดต DataStore เป็นระยะ

ตัวอย่างนี้ใช้ JSON DataStore แต่คุณยังใช้ Preferences หรือ Proto DataStore ได้ด้วย

@Serializable
data class Time(
    val lastUpdateMillis: Long
)

Serializer จะบอก DataStore ถึงวิธีอ่านและเขียนประเภทข้อมูลของคุณ ตรวจสอบว่าคุณได้ใส่ค่าเริ่มต้นสำหรับ Serializer ที่จะใช้หากยังไม่มีการสร้างไฟล์ ต่อไปนี้คือตัวอย่างการติดตั้งใช้งานโดยใช้ 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()
        )
    }
}

หากต้องการใช้ DataStore ในกระบวนการต่างๆ คุณต้องสร้าง ออบเจ็กต์ DataStore โดยใช้ MultiProcessDataStoreFactory สำหรับทั้งโค้ดแอป และโค้ดบริการ

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

เพิ่มข้อมูลต่อไปนี้ลงใน AndroidManifiest.xml

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

บริการจะเรียกใช้ updateLastUpdateTime เป็นระยะ ซึ่งจะเขียนลงในพื้นที่เก็บข้อมูล โดยใช้ updateData

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

แอปจะอ่านค่าที่บริการเขียนโดยใช้โฟลว์ข้อมูลดังนี้

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

ตอนนี้เราสามารถรวมฟังก์ชันทั้งหมดนี้ไว้ในคลาสที่ชื่อว่า MultiProcessDataStore และใช้ในแอปได้แล้ว

นี่คือโค้ดบริการ

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()
    }
}

และโค้ดแอป

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)
    }
}

คุณสามารถใช้ Hilt การแทรกทรัพยากร Dependency เพื่อให้อินสแตนซ์ DataStore ไม่ซ้ำกันในแต่ละกระบวนการ

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

จัดการไฟล์เสียหาย

ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก ไฟล์ที่เก็บไว้ในดิสก์ของ DataStore อาจเสียหาย โดยค่าเริ่มต้น DataStore จะไม่กู้คืนจากความเสียหายโดยอัตโนมัติ และการพยายามอ่านจาก DataStore จะทำให้ระบบแสดง CorruptionException

DataStore มี Corruption Handler API ที่ช่วยให้คุณกู้คืนได้อย่างราบรื่นในสถานการณ์ดังกล่าว และหลีกเลี่ยงการแสดงข้อยกเว้น เมื่อกำหนดค่าแล้ว Corruption Handler จะแทนที่ไฟล์ที่เสียหายด้วยไฟล์ใหม่ที่มีค่าเริ่มต้นที่กำหนดไว้ล่วงหน้า

หากต้องการตั้งค่า Handler นี้ ให้ระบุ corruptionHandler เมื่อสร้างอินสแตนซ์ DataStore ใน by dataStore หรือในเมธอด Factory ของ DataStoreFactory

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

แสดงความคิดเห็น

แชร์ความคิดเห็นและไอเดียกับเราผ่านแหล่งข้อมูลต่อไปนี้

เครื่องมือติดตามปัญหา:
รายงานปัญหาเพื่อให้เราแก้ไขข้อบกพร่องได้

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Jetpack DataStore ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

ตัวอย่าง

บล็อก

Codelab