DataStore   Android Jetpack का हिस्सा है.

Kotlin Multiplatform का इस्तेमाल करके देखें
Kotlin Multiplatform की मदद से, डेटा लेयर को अन्य प्लैटफ़ॉर्म के साथ शेयर किया जा सकता है. KMP में DataStore को सेट अप करने और उसका इस्तेमाल करने का तरीका जानें

Jetpack DataStore, डेटा स्टोरेज का एक समाधान है. इसकी मदद से, की-वैल्यू पेयर या टाइप किए गए ऑब्जेक्ट को प्रोटोकॉल बफ़र के साथ सेव किया जा सकता है. DataStore, डेटा को एसिंक्रोनस तरीके से, लगातार, और लेन-देन के हिसाब से स्टोर करने के लिए Kotlin कोरोटीन और फ़्लो का इस्तेमाल करता है.

अगर डेटा सेव करने के लिए SharedPreferences का इस्तेमाल किया जा रहा है, तो DataStore पर माइग्रेट करें.

DataStore API

DataStore इंटरफ़ेस, यह एपीआई उपलब्ध कराता है:

  1. ऐसा फ़्लो जिसका इस्तेमाल DataStore से डेटा पढ़ने के लिए किया जा सकता है

    val data: Flow<T>
    
  2. DataStore में डेटा अपडेट करने के लिए फ़ंक्शन

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

DataStore कॉन्फ़िगरेशन

अगर आपको कुंजियों का इस्तेमाल करके डेटा को स्टोर और ऐक्सेस करना है, तो Preferences DataStore का इस्तेमाल करें. इसके लिए, पहले से तय किए गए स्कीमा की ज़रूरत नहीं होती. साथ ही, यह टाइप सेफ़्टी की सुविधा नहीं देता. इसमें SharedPreferences जैसा एपीआई है, लेकिन इसमें शेयर की गई प्राथमिकताओं से जुड़ी कमियां नहीं हैं.

DataStore की मदद से, कस्टम क्लास को सेव किया जा सकता है. इसके लिए, आपको डेटा के लिए स्कीमा तय करना होगा. साथ ही, इसे सेव किए जा सकने वाले फ़ॉर्मैट में बदलने के लिए, Serializer देना होगा. प्रोटोकॉल बफ़र, JSON या किसी अन्य सीरियलाइज़ेशन रणनीति का इस्तेमाल किया जा सकता है.

सेटअप

अपने ऐप्लिकेशन में Jetpack DataStore का इस्तेमाल करने के लिए, अपनी Gradle फ़ाइल में यह जानकारी जोड़ें. यह इस बात पर निर्भर करती है कि आपको कौनसी सुविधा इस्तेमाल करनी है:

Preferences DataStore

अपनी gradle फ़ाइल के dependencies सेक्शन में ये लाइनें जोड़ें:

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 का इस्तेमाल करने के लिए, ये डिपेंडेंसी जोड़ें:

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

अपनी gradle फ़ाइल के dependencies सेक्शन में ये लाइनें जोड़ें:

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

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

कॉन्टेंट को क्रम से लगाने के लिए, प्रोटोकॉल बफ़र या 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")
    }
    

प्रोटोबफ़ सीरियलाइज़ेशन

प्रोटोबफ़ सीरियलाइज़ेशन का इस्तेमाल करने के लिए, अपनी 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 का एक से ज़्यादा इंस्टेंस कभी न बनाएं. ऐसा करने से, DataStore की सभी सुविधाएं काम करना बंद कर सकती हैं. अगर एक ही प्रोसेस में किसी फ़ाइल के लिए एक से ज़्यादा DataStore चालू हैं, तो डेटा को पढ़ने या अपडेट करने के दौरान DataStore IllegalStateException दिखाएगा.

  2. DataStore<T> का जेनरिक टाइप, बदला नहीं जा सकने वाला होना चाहिए. DataStore में इस्तेमाल किए गए टाइप में बदलाव करने से, DataStore की ओर से उपलब्ध कराई गई स्थिरता खत्म हो जाती है. साथ ही, इससे गंभीर बग पैदा हो सकते हैं जिन्हें ठीक करना मुश्किल होता है. हमारा सुझाव है कि आप प्रोटोकॉल बफ़र का इस्तेमाल करें. इससे यह पक्का करने में मदद मिलती है कि डेटा में बदलाव नहीं किया जा सकता. साथ ही, इससे एपीआई को समझने में आसानी होती है और डेटा को सीरियल करने में कम समय लगता है.

  3. एक ही फ़ाइल के लिए, SingleProcessDataStore और MultiProcessDataStore का इस्तेमाल एक साथ न करें. अगर आपको एक से ज़्यादा प्रोसेस से DataStore को ऐक्सेस करना है, तो आपको MultiProcessDataStore का इस्तेमाल करना होगा.

डेटा की परिभाषा

Preferences DataStore

ऐसी कुंजी तय करें जिसका इस्तेमाल डिस्क पर डेटा को सेव करने के लिए किया जाएगा.

val EXAMPLE_COUNTER = intPreferencesKey("example_counter")

JSON डेटास्टोर

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 के लिए, app/src/main/proto/ डायरेक्ट्री में मौजूद किसी प्रोटो फ़ाइल में पहले से तय किया गया स्कीमा होना ज़रूरी है. यह स्कीमा, उन ऑब्जेक्ट के टाइप के बारे में बताता है जिन्हें Proto DataStore में सेव किया जाता है. प्रोटो स्कीमा तय करने के बारे में ज़्यादा जानने के लिए, प्रोटोबफ़ लैंग्वेज गाइड देखें.

src/main/proto फ़ोल्डर में settings.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 आपके डेटा टाइप को कैसे पढ़ेगा और लिखेगा. पक्का करें कि आपने सीरियलाइज़र के लिए डिफ़ॉल्ट वैल्यू शामिल की हो, ताकि अगर अब तक कोई फ़ाइल नहीं बनाई गई है, तो उसका इस्तेमाल किया जा सके.

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 क्लास का इस्तेमाल किया जाता है. इससे की-वैल्यू पेयर को डिस्क में सेव किया जा सकता है. DataStore<Preferences> का इंस्टेंस बनाने के लिए, preferencesDataStore से बनाए गए प्रॉपर्टी डेलिगेट का इस्तेमाल करें. इसे Kotlin फ़ाइल के टॉप लेवल पर एक बार कॉल करें. अपने पूरे ऐप्लिकेशन में, इस प्रॉपर्टी के ज़रिए DataStore को ऐक्सेस करें. इससे, DataStore को सिंगलटन के तौर पर बनाए रखना आसान हो जाता है. name पैरामीटर का इस्तेमाल करना ज़रूरी है. यह Preferences DataStore का नाम होता है.

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

JSON डेटास्टोर

dataStore से बनाए गए प्रॉपर्टी डेलिगेट का इस्तेमाल करके, DataStore<T> का इंस्टेंस बनाएं. यहां T, सीरियलाइज़ किया जा सकने वाला डेटा क्लास है. इसे अपनी Kotlin फ़ाइल के टॉप लेवल पर एक बार कॉल करें और अपने ऐप्लिकेशन के बाकी हिस्से में, इस प्रॉपर्टी डेलिगेट के ज़रिए इसे ऐक्सेस करें. fileName पैरामीटर, DataStore को बताता है कि डेटा सेव करने के लिए किस फ़ाइल का इस्तेमाल करना है. साथ ही, serializer पैरामीटर, DataStore को पहले से तय किए गए सीरियलाइज़र क्लास का नाम बताता है.

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

Proto DataStore

dataStore से बनाए गए प्रॉपर्टी डेलिगेट का इस्तेमाल करके, DataStore<T> का इंस्टेंस बनाएं. यहां T, प्रोटो फ़ाइल में तय किया गया टाइप है. इसे अपनी Kotlin फ़ाइल के टॉप लेवल पर एक बार कॉल करें और अपने ऐप्लिकेशन के बाकी हिस्से में, इस प्रॉपर्टी डेलिगेट के ज़रिए इसे ऐक्सेस करें. fileName पैरामीटर, DataStore को बताता है कि डेटा सेव करने के लिए किस फ़ाइल का इस्तेमाल करना है. वहीं, serializer पैरामीटर, DataStore को पहले से तय किए गए सीरियलाइज़र क्लास का नाम बताता है.

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 डेटास्टोर

स्टोर किए गए ऑब्जेक्ट से सही प्रॉपर्टी का Flow दिखाने के लिए, DataStore.data का इस्तेमाल करें.

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

Proto DataStore

स्टोर किए गए ऑब्जेक्ट से सही प्रॉपर्टी का Flow दिखाने के लिए, DataStore.data का इस्तेमाल करें.

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

कंपोज़ेबल में, ViewModel से जनरेट किए गए Flow का इस्तेमाल करने के लिए, collectAsStateWithLifecycle का इस्तेमाल करें. यह DataStore फ़्लो को Compose स्टेट में सुरक्षित तरीके से बदलता है. इससे रीयूज़र इंटरफ़ेस फिर से कंपोज़ होता है.

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

collectAsStateWithLifecycle के बारे में ज़्यादा जानने के लिए, स्टेट और 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 डेटास्टोर

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 ऐप्लिकेशन में DataStore का इस्तेमाल करना

Compose ऐप्लिकेशन में DataStore का इस्तेमाल करने के लिए, Android ऐप्लिकेशन आर्किटेक्चर के दिशा-निर्देशों का पालन करें. इसके लिए, DataStore के ऑपरेशन को अपने डेटा लेयर (जैसे कि रिपॉज़िटरी) में रखें. साथ ही, ViewModel के ज़रिए अपने यूज़र इंटरफ़ेस (यूआई) को डेटा दिखाएं.

कंपोज़ेबल फ़ंक्शन में, सीधे DataStore से डेटा पढ़ने या उसमें डेटा लिखने से बचें.

  1. ViewModel के ज़रिए DataStore को ऐक्सेस करना. अपनी रिपॉज़िटरी (जो DataStore को रैप करती है) को ViewModel में पास करें और Flow को StateFlow में बदलें, ताकि यूज़र इंटरफ़ेस इसे आसानी से देख सके. इसे नीचे दिए गए स्निपेट में दिखाया गया है:

    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. अपने कंपोज़ेबल से डेटा को ऑब्ज़र्व करना और लिखना. अपने यूज़र इंटरफ़ेस (यूआई) में StateFlow को सुरक्षित तरीके से देखने के लिए, collectAsStateWithLifecycle का इस्तेमाल करें. साथ ही, लिखने की प्रोसेस को मैनेज करने के लिए, 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 डेटास्टोर का इस्तेमाल किया गया है. हालांकि, Preferences या Proto DataStore का इस्तेमाल भी किया जा सकता है.

@Serializable
data class Time(
    val lastUpdateMillis: Long
)

सीरियलाइज़र, DataStore को बताता है कि आपके डेटा टाइप को कैसे पढ़ा और लिखा जाए. पक्का करें कि आपने सीरियलाइज़र के लिए डिफ़ॉल्ट वैल्यू शामिल की हो, ताकि अगर अब तक कोई फ़ाइल नहीं बनाई गई है, तो उसका इस्तेमाल किया जा सके. यहां 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 का इस्तेमाल करके 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 डिपेंडेंसी इंजेक्शन का इस्तेमाल किया जा सकता है, ताकि हर प्रोसेस के लिए आपका DataStore इंस्टेंस यूनीक हो:

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

फ़ाइल खराब होने की समस्या को ठीक करना

बहुत कम मामलों में, DataStore की डिस्क पर सेव की गई फ़ाइल खराब हो सकती है. डिफ़ॉल्ट रूप से, DataStore में डेटा खराब होने पर, वह अपने-आप ठीक नहीं होता. साथ ही, इससे डेटा पढ़ने की कोशिश करने पर, सिस्टम CorruptionException दिखाएगा.

DataStore, डेटा करप्ट होने की समस्या को ठीक करने वाला एपीआई उपलब्ध कराता है. इससे आपको इस तरह की समस्या को ठीक करने में मदद मिल सकती है. साथ ही, अपवाद से बचा जा सकता है. कॉन्फ़िगर किए जाने पर, डेटा करप्शन हैंडलर, खराब हुई फ़ाइल को एक नई फ़ाइल से बदल देता है. इस नई फ़ाइल में पहले से तय की गई डिफ़ॉल्ट वैल्यू होती है.

इस हैंडलर को सेट अप करने के लिए, by dataStore में DataStore इंस्टेंस बनाते समय या DataStoreFactory फ़ैक्ट्री मेथड में corruptionHandler दें:

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

सुझाव/राय दें या शिकायत करें

इन संसाधनों के ज़रिए, अपने सुझाव/राय दें या शिकायत करें:

समस्या को ट्रैक करने वाला टूल:
समस्याओं की शिकायत करें, ताकि हम बग ठीक कर सकें.

अन्य संसाधन

Jetpack DataStore के बारे में ज़्यादा जानने के लिए, यहां दिए गए अन्य संसाधन देखें:

सैंपल

ब्लॉग

कोडलैब