DataStore חלק מ-Android Jetpack.
Jetpack DataStore הוא פתרון לאחסון נתונים שמאפשר לכם לאחסן זוגות של מפתח/ערך או אובייקטים מוקלדים באמצעות מאגרי פרוטוקולים. DataStore משתמש ב-Kotlin coroutines וב-Flow כדי לאחסן נתונים באופן אסינכרוני, עקבי וטרנזקציונלי.
אם אתם משתמשים ב-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
מוסיפים את השורות הבאות לחלק של יחסי התלות בקובץ gradle:
Groovy
dependencies { // Preferences DataStore (SharedPreferences like APIs) implementation "androidx.datastore:datastore-preferences:1.2.0" // Alternatively - without an Android dependency. implementation "androidx.datastore:datastore-preferences-core:1.2.0" }
Kotlin
dependencies { // Preferences DataStore (SharedPreferences like APIs) implementation("androidx.datastore:datastore-preferences:1.2.0") // Alternatively - without an Android dependency. implementation("androidx.datastore:datastore-preferences-core:1.2.0") }
כדי להוסיף תמיכה אופציונלית ב-RxJava, מוסיפים את יחסי התלות הבאים:
Groovy
dependencies { // optional - RxJava2 support implementation "androidx.datastore:datastore-preferences-rxjava2:1.2.0" // optional - RxJava3 support implementation "androidx.datastore:datastore-preferences-rxjava3:1.2.0" }
Kotlin
dependencies { // optional - RxJava2 support implementation("androidx.datastore:datastore-preferences-rxjava2:1.2.0") // optional - RxJava3 support implementation("androidx.datastore:datastore-preferences-rxjava3:1.2.0") }
DataStore
מוסיפים את השורות הבאות לחלק של יחסי התלות בקובץ gradle:
Groovy
dependencies { // Typed DataStore for custom data objects (for example, using Proto or JSON). implementation "androidx.datastore:datastore:1.2.0" // Alternatively - without an Android dependency. implementation "androidx.datastore:datastore-core:1.2.0" }
Kotlin
dependencies { // Typed DataStore for custom data objects (for example, using Proto or JSON). implementation("androidx.datastore:datastore:1.2.0") // Alternatively - without an Android dependency. implementation("androidx.datastore:datastore-core:1.2.0") }
מוסיפים את יחסי התלות האופציונליים הבאים לתמיכה ב-RxJava:
Groovy
dependencies { // optional - RxJava2 support implementation "androidx.datastore:datastore-rxjava2:1.2.0" // optional - RxJava3 support implementation "androidx.datastore:datastore-rxjava3:1.2.0" }
Kotlin
dependencies { // optional - RxJava2 support implementation("androidx.datastore:datastore-rxjava2:1.2.0") // optional - RxJava3 support implementation("androidx.datastore:datastore-rxjava3:1.2.0") }
כדי לבצע סריאליזציה של תוכן, מוסיפים תלות בסריאליזציה של 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 בצורה נכונה, חשוב לזכור תמיד את הכללים הבאים:
לעולם אל תיצרו יותר ממופע אחד של
DataStoreעבור קובץ נתון באותו תהליך. פעולה כזו עלולה לשבור את כל הפונקציונליות של DataStore. אם יש כמה DataStore פעילים לקובץ נתון באותו תהליך, DataStore יחזירIllegalStateExceptionכשקוראים או מעדכנים נתונים.הסוג הגנרי של
DataStore<T>חייב להיות בלתי ניתן לשינוי. שינוי של סוג שמשמש ב-DataStore מבטל את העקביות ש-DataStore מספק, ויוצר באגים שעלולים להיות חמורים וקשים לאיתור. מומלץ להשתמש ב-protocol buffers, שעוזרים להבטיח אי-שינוי, API ברור וסדרות יעילות.אל תערבבו בין שימושים במאפיינים
SingleProcessDataStoreו-MultiProcessDataStoreבאותו קובץ. אם אתם מתכוונים לגשת אלDataStoreמיותר מתהליך אחד, אתם צריכים להשתמש ב-MultiProcessDataStore.
הגדרת נתונים
Preferences DataStore
הגדרת מפתח שישמש לשמירת נתונים בדיסק.
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
JSON DataStore
במאגר נתונים מסוג JSON, מוסיפים הערה @Serialization לנתונים שרוצים לשמור
@Serializable
data class Settings(
val exampleCounter: Int
)
מגדירים מחלקה שמטמיעה את Serializer<T>, כאשר T הוא הסוג של המחלקה שאליה הוספתם את ההערה הקודמת. חשוב להוסיף ערך ברירת מחדל לסריאליזציה, שישמש אם עדיין לא נוצר קובץ.
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 הוא הסוג שמוגדר בקובץ הפרוטו. מחלקת הסריאליזציה הזו מגדירה איך 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)
}
}
יצירת מאגר נתונים
צריך לציין שם לקובץ שמשמש לשמירת הנתונים.
Preferences DataStore
ההטמעה של Preferences DataStore משתמשת במחלקות DataStore ו-Preferences כדי לשמור צמדי מפתח-ערך בדיסק. משתמשים בנציג המאפיין שנוצר על ידי preferencesDataStore כדי ליצור מופע של DataStore<Preferences>. קוראים לה פעם אחת ברמה העליונה של קובץ Kotlin. גישה ל-DataStore דרך הנכס הזה בכל שאר חלקי האפליקציה. כך קל יותר לשמור על DataStore כ-singleton.
אפשרות אחרת היא להשתמש ב-RxPreferenceDataStoreBuilder אם אתם משתמשים ב-RxJava.
הפרמטר name הוא חובה והוא השם של מאגר נתוני ההעדפות.
// 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 את השם של מחלקת הסריאליזציה שהוגדרה בשלב 1.
val Context.dataStore: DataStore<Settings> by dataStore(
fileName = "settings.json",
serializer = SettingsSerializer,
)
Proto DataStore
משתמשים בנציג המאפיין שנוצר על ידי dataStore כדי ליצור מופע של DataStore<T>, כאשר T הוא הסוג שמוגדר בקובץ הפרוטו. קוראים לה פעם אחת ברמה העליונה של קובץ Kotlin וניגשים אליה דרך נציג המאפיין הזה בכל שאר האפליקציה. הפרמטר fileName מציין ל-DataStore באיזה קובץ להשתמש כדי לאחסן את הנתונים, והפרמטר serializer מציין ל-DataStore את השם של מחלקת הסריאליזציה שהוגדרה בשלב 1.
val Context.dataStore: DataStore<Settings> by dataStore(
fileName = "settings.pb",
serializer = SettingsSerializer,
)
קריאה מ-DataStore
צריך לציין שם לקובץ שמשמש לשמירת הנתונים.
Preferences DataStore
מכיוון ש-Preferences DataStore לא משתמש בסכימה מוגדרת מראש, צריך להשתמש בפונקציה של סוג המפתח המתאים כדי להגדיר מפתח לכל ערך שרוצים לאחסן במופע DataStore<Preferences>. לדוגמה, כדי להגדיר מפתח לערך int, משתמשים ב-