โปรเจ็กต์: /architecture/_project.yaml หนังสือ: /architecture/_book.yaml คำหลัก: datastore, architecture, api:JetpackDataStore คำอธิบาย: สำรวจคู่มือสถาปัตยกรรมแอปนี้ในไลบรารีเลเยอร์ข้อมูลเพื่อดูข้อมูลเกี่ยวกับ Preferences DataStore และ Proto DataStore, การตั้งค่า และอื่นๆ hide_page_heading: true
DataStore ส่วนหนึ่งของ Android Jetpack
Jetpack DataStore เป็นโซลูชันพื้นที่เก็บข้อมูลที่ช่วยให้คุณจัดเก็บคู่คีย์-ค่าหรือออบเจ็กต์ที่มีการพิมพ์ด้วยบัฟเฟอร์โปรโตคอลได้ DataStore ใช้โครูทีนและ Flow ของ Kotlin เพื่อจัดเก็บข้อมูลแบบไม่พร้อมกัน อย่างสม่ำเสมอ และแบบทรานแซ็กชัน
หากใช้ SharedPreferences เพื่อจัดเก็บข้อมูล ให้พิจารณาย้ายข้อมูลไปยัง DataStore แทน
DataStore API
อินเทอร์เฟซ DataStore มี API ดังนี้
โฟลว์ที่ใช้เพื่ออ่านข้อมูลจาก DataStore ได้
val data: Flow<T>ฟังก์ชันสำหรับอัปเดตข้อมูลใน DataStore
suspend updateData(transform: suspend (t) -> T)
การกำหนดค่า DataStore
หากต้องการจัดเก็บและเข้าถึงข้อมูลโดยใช้คีย์ ให้ใช้การติดตั้งใช้งาน Preferences
DataStore ซึ่งไม่ต้องมีสคีมาที่กำหนดไว้ล่วงหน้า และไม่มี
การรับประกันความปลอดภัยของประเภท มี API ที่คล้ายกับ SharedPreferences แต่ไม่มีข้อเสียที่เกี่ยวข้องกับค่ากำหนดที่แชร์
DataStore ช่วยให้คุณบันทึกคลาสที่กำหนดเองได้ โดยคุณต้องกำหนด
สคีมาสำหรับข้อมูลและระบุ Serializer เพื่อแปลงข้อมูลเป็นรูปแบบที่คงอยู่ได้
คุณเลือกใช้ Protocol Buffers, JSON หรือกลยุทธ์การซีเรียลไลซ์อื่นๆ ได้
ตั้งค่า
หากต้องการใช้ Jetpack DataStore ในแอป ให้เพิ่มข้อมูลต่อไปนี้ลงในไฟล์ Gradle โดยขึ้นอยู่กับการติดตั้งใช้งานที่คุณต้องการใช้
Preferences DataStore
Add the following lines to the dependencies part of your gradle file:
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") }
To add optional RxJava support, add the following dependencies:
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
Add the following lines to the dependencies part of your gradle file:
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") }
Add the following optional dependencies for RxJava support:
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") }
To serialize content, add dependencies for either Protocol Buffers or JSON serialization.
JSON serialization
To use JSON serialization, add the following to your Gradle file:
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 serialization
To use Protobuf serialization, add the following to your Gradle file:
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 ได้อย่างถูกต้อง
อย่าสร้างอินสแตนซ์ของ
DataStoreมากกว่า 1 รายการสำหรับไฟล์ที่กำหนดใน กระบวนการเดียวกัน การทำเช่นนี้อาจทำให้ฟังก์ชันการทำงานทั้งหมดของ DataStore หยุดทำงาน หากมี DataStore หลายรายการที่ใช้งานอยู่สำหรับไฟล์หนึ่งๆ ในกระบวนการเดียวกัน DataStore จะแสดงข้อผิดพลาดIllegalStateExceptionเมื่ออ่านหรืออัปเดตข้อมูลประเภททั่วไปของ
DataStore<T>ต้องเปลี่ยนแปลงไม่ได้ การเปลี่ยนแปลงประเภท ที่ใช้ใน DataStore จะทำให้ความสอดคล้องที่ DataStore มีอยู่ไม่ถูกต้อง และ อาจทำให้เกิดข้อบกพร่องที่ร้ายแรงและตรวจจับได้ยาก เราขอแนะนำให้คุณใช้ บัฟเฟอร์โปรโตคอล ซึ่งจะช่วยให้มั่นใจได้ถึงความคงที่ API ที่ชัดเจน และการ ซีเรียลไลซ์ที่มีประสิทธิภาพอย่าใช้
SingleProcessDataStoreและMultiProcessDataStoreร่วมกันสำหรับไฟล์เดียวกัน หากต้องการเข้าถึงDataStoreจากกระบวนการมากกว่า 1 รายการ คุณต้องใช้MultiProcessDataStore
คำจำกัดความของข้อมูล
Preferences DataStore
กำหนดคีย์ที่จะใช้เพื่อบันทึกข้อมูลลงในดิสก์
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
JSON DataStore
สำหรับที่เก็บข้อมูล JSON ให้เพิ่มคำอธิบายประกอบ @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 ต้องมีสคีมาที่กำหนดไว้ล่วงหน้าในไฟล์ Proto ในไดเรกทอรี
app/src/main/proto/ สคีมานี้กำหนดประเภทของออบเจ็กต์
ที่คุณคงไว้ใน Proto DataStore ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดสคีมาโปรโต
ได้ที่คู่มือภาษา Protobuf
เพิ่มไฟล์ชื่อ 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 คือประเภทที่กำหนด
ในไฟล์ Proto คลาส 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
การใช้งาน DataStore ของค่ากําหนดจะใช้คลาส DataStore และ
Preferences เพื่อบันทึกคู่คีย์-ค่าลงในดิสก์ ใช้ตัวแทนพร็อพเพอร์ตี้ที่สร้างโดย preferencesDataStore เพื่อสร้างอินสแตนซ์
ของ DataStore<Preferences> เรียกใช้ 1 ครั้งที่ระดับบนสุดของไฟล์ Kotlin
เข้าถึง DataStore ผ่านพร็อพเพอร์ตี้นี้ตลอดทั้งแอปพลิเคชันที่เหลือ
ซึ่งจะช่วยให้คุณเก็บ DataStore ไว้เป็น Singleton ได้ง่ายขึ้น
หรือใช้ RxPreferenceDataStoreBuilder หากคุณใช้ RxJava
พารามิเตอร์ name ที่จำเป็นคือชื่อของ Preferences DataStore
// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
JSON DataStore
ใช้ผู้มอบสิทธิ์พร็อพเพอร์ตี้ที่สร้างโดย dataStore เพื่อสร้างอินสแตนซ์ของ
DataStore<T> โดยที่ T คือคลาสข้อมูลที่แปลงเป็นอนุกรมได้ เรียกใช้ครั้งเดียว
ที่ระดับบนสุดของไฟล์ Kotlin และเข้าถึงผ่านพร็อพเพอร์ตี้นี้
ตลอดทั้งแอป พารามิเตอร์ fileName จะบอก
DataStore ว่าจะใช้ไฟล์ใดในการจัดเก็บข้อมูล และพารามิเตอร์ serializer จะบอก
DataStore ว่าชื่อของคลาส Serializer ที่กำหนดไว้ในขั้นตอนที่ 1 คืออะไร
val Context.dataStore: DataStore<Settings> by dataStore(
fileName = "settings.json",
serializer = SettingsSerializer,
)
Proto DataStore
ใช้ตัวแทนพร็อพเพอร์ตี้ที่สร้างโดย dataStore เพื่อสร้างอินสแตนซ์ของ
DataStore<T> โดยที่ T คือประเภทที่กำหนดไว้ในไฟล์ Proto เรียกใช้
ครั้งเดียวที่ระดับบนสุดของไฟล์ Kotlin และเข้าถึงผ่านตัวแทนพร็อพเพอร์ตี้นี้
ตลอดทั้งแอป พารามิเตอร์ fileName จะบอก
DataStore ว่าจะใช้ไฟล์ใดในการจัดเก็บข้อมูล และพารามิเตอร์ serializer จะบอก
DataStore ถึงชื่อของคลาส Serializer ที่กำหนดไว้ในขั้นตอนที่ 1
val Context.dataStore: DataStore<Settings> by dataStore(
fileName = "settings.pb",
serializer = SettingsSerializer,
)
อ่านจาก DataStore
คุณต้องระบุชื่อไฟล์ที่ใช้เพื่อคงข้อมูลไว้
Preferences DataStore
เนื่องจาก Preferences DataStore ไม่ได้ใช้สคีมาที่กำหนดไว้ล่วงหน้า คุณจึงต้องใช้ฟังก์ชันประเภทคีย์ที่เกี่ยวข้องเพื่อกำหนดคีย์สำหรับแต่ละค่าที่ต้องการจัดเก็บในอินสแตนซ์ DataStore<Preferences> เช่น หากต้องการกำหนดคีย์สำหรับค่า int ให้ใช้ 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
}
เขียนไปยัง 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 }
}
}
ตัวอย่างการเขียน
คุณสามารถรวมฟังก์ชันเหล่านี้ไว้ในคลาสเดียวและใช้ในแอป Compose ได้
Preferences DataStore
ตอนนี้เราสามารถใส่ฟังก์ชันเหล่านี้ลงในคลาสที่ชื่อ PreferencesDataStore และ
ใช้ในแอป 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")
}
JSON DataStore
ตอนนี้เราสามารถใส่ฟังก์ชันเหล่านี้ลงในคลาสที่ชื่อ JSONDataStore และใช้ในแอป 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
ตอนนี้เราสามารถใส่ฟังก์ชันเหล่านี้ลงในคลาสที่ชื่อ ProtoDataStore และใช้ในแอป 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")
}
ใช้ DataStore ในโค้ดแบบซิงโครนัส
ประโยชน์หลักอย่างหนึ่งของ DataStore คือ API แบบอะซิงโครนัส แต่การเปลี่ยนโค้ดโดยรอบให้เป็นแบบอะซิงโครนัสอาจทำไม่ได้เสมอไป กรณีนี้อาจเกิดขึ้นหากคุณทำงานกับโค้ดเบสที่มีอยู่ซึ่งใช้ I/O ของดิสก์แบบซิงโครนัส หรือหากคุณมี Dependency ที่ไม่ได้ให้ API แบบอะซิงโครนัส
โคโรทีน Kotlin มีตัวสร้างโคโรทีน runBlocking() เพื่อช่วย
เชื่อมช่องว่างระหว่างโค้ดแบบซิงโครนัสและแบบอะซิงโครนัส คุณใช้
runBlocking()เพื่ออ่านข้อมูลจาก DataStore แบบซิงโครนัสได้ RxJava มีเมธอดที่บล็อกใน Flowable โค้ดต่อไปนี้จะบล็อกเธรดที่เรียกใช้
จนกว่า DataStore จะแสดงข้อมูล
Kotlin
val exampleData = runBlocking { context.dataStore.data.first() }
Java
Settings settings = dataStore.data().blockingFirst();
การดำเนินการ I/O แบบซิงโครนัสในเทรด UI อาจทำให้เกิด ANR หรือ UI ที่ไม่ตอบสนอง คุณสามารถลดปัญหาเหล่านี้ได้โดยการโหลดข้อมูลจาก DataStore ล่วงหน้าแบบไม่พร้อมกัน ดังนี้
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
context.dataStore.data.first()
// You should also handle IOExceptions here.
}
}
Java
dataStore.data().first().subscribe();
ด้วยวิธีนี้ DataStore จะอ่านข้อมูลแบบไม่พร้อมกันและแคชข้อมูลไว้ในหน่วยความจำ การอ่านแบบซิงโครนัสในภายหลังโดยใช้ runBlocking() อาจเร็วกว่าหรืออาจหลีกเลี่ยงการดำเนินการ I/O ของดิสก์
ทั้งหมดได้หากการอ่านครั้งแรกเสร็จสมบูรณ์แล้ว
ใช้ DataStore ในโค้ดแบบหลายกระบวนการ
คุณสามารถกําหนดค่า DataStore เพื่อเข้าถึงข้อมูลเดียวกันในกระบวนการต่างๆ โดยมีพร็อพเพอร์ตี้ความสอดคล้องของข้อมูลเดียวกันกับที่อยู่ภายในกระบวนการเดียว โดยเฉพาะอย่างยิ่ง DataStore มีบริการต่อไปนี้
- การอ่านจะแสดงเฉพาะข้อมูลที่บันทึกลงในดิสก์เท่านั้น
- ความสอดคล้องแบบ Read-After-Write
- ระบบจะจัดลำดับการเขียน
- การอ่านจะไม่ถูกบล็อกโดยการเขียน
ลองพิจารณาแอปพลิเคชันตัวอย่างที่มีบริการและกิจกรรมซึ่งบริการ ทำงานในกระบวนการแยกต่างหากและอัปเดต DataStore เป็นระยะ
ตัวอย่างนี้ใช้ที่เก็บข้อมูล JSON แต่คุณจะใช้ที่เก็บข้อมูลการตั้งค่าหรือที่เก็บข้อมูล Proto ก็ได้
@Serializable
data class Time(
val lastUpdateMillis: Long
)
ซีเรียลไลเซอร์จะบอก 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.cacheDir.path}/time.pb")
},
corruptionHandler = null
)
เพิ่มโค้ดต่อไปนี้ใน AndroidManifiest.xml
<service
android:name=".TimestampUpdateService"
android:process=":my_process_id" />
บริการจะเรียกใช้ updateLastUpdateTime() เป็นระยะๆ ซึ่งจะเขียนไปยัง
Datastore โดยใช้ 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)
}
}
คุณสามารถใช้การแทรกทรัพยากร Dependency ของ Hilt เพื่อให้อินสแตนซ์ DataStore ไม่ซ้ำกันต่อกระบวนการ
@Provides
@Singleton
fun provideDataStore(@ApplicationContext context: Context): DataStore<Settings> =
MultiProcessDataStoreFactory.create(...)
จัดการไฟล์ที่เสียหาย
ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก ไฟล์ที่คงอยู่บนดิสก์ของ DataStore อาจเสียหาย โดยค่าเริ่มต้น DataStore จะไม่กู้คืนจากการเสียหายโดยอัตโนมัติ
และการพยายามอ่านจาก DataStore จะทำให้ระบบแสดงCorruptionException
DataStore มี API ตัวแฮนเดิลความเสียหายที่จะช่วยให้คุณกู้คืนได้อย่างราบรื่น ในสถานการณ์ดังกล่าว และหลีกเลี่ยงการทิ้งข้อยกเว้น เมื่อกำหนดค่าแล้ว ตัวแฮนเดิลการเสียหายจะแทนที่ไฟล์ที่เสียหายด้วยไฟล์ใหม่ที่มี ค่าเริ่มต้นที่กำหนดไว้ล่วงหน้า
หากต้องการตั้งค่าแฮนเดิลนี้ ให้ระบุ corruptionHandler เมื่อสร้างอินสแตนซ์ DataStore ใน by dataStore() หรือในเมธอด DataStoreFactory ของ Factory
val dataStore: DataStore<Settings> = DataStoreFactory.create(
serializer = SettingsSerializer(),
produceFile = {
File("${context.cacheDir.path}/myapp.preferences_pb")
},
corruptionHandler = ReplaceFileCorruptionHandler { Settings(lastUpdate = 0) }
)
แสดงความคิดเห็น
แชร์ความคิดเห็นและไอเดียกับเราผ่านแหล่งข้อมูลต่อไปนี้
- เครื่องมือติดตามปัญหา:
- รายงานปัญหาเพื่อให้เราแก้ไขข้อบกพร่องได้
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Jetpack DataStore ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้
ตัวอย่าง
บล็อก
Codelabs
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- โหลดและแสดงข้อมูลแบบแบ่งหน้า
- ภาพรวม LiveData
- เลย์เอาต์และการเชื่อมโยงนิพจน์