ডেটাস্টোর অ্যান্ড্রয়েড জেটপ্যাকের অংশ।

কোটলিন মাল্টিপ্ল্যাটফর্ম দিয়ে চেষ্টা করুন
কোটলিন মাল্টিপ্ল্যাটফর্ম অন্যান্য প্ল্যাটফর্মের সাথে ডেটা লেয়ার শেয়ার করার সুযোগ দেয়। KMP তে ডেটাস্টোর কীভাবে সেট আপ করবেন এবং কীভাবে কাজ করবেন তা শিখুন।

জেটপ্যাক ডেটাস্টোর হল একটি ডেটা স্টোরেজ সলিউশন যা আপনাকে প্রোটোকল বাফারের সাহায্যে কী-মান জোড়া বা টাইপ করা বস্তু সংরক্ষণ করতে দেয়। ডেটাস্টোর অ্যাসিঙ্ক্রোনাস, ধারাবাহিকভাবে এবং লেনদেনের মাধ্যমে ডেটা সংরক্ষণ করতে কোটলিন কর্উটিন এবং ফ্লো ব্যবহার করে।

যদি আপনি ডেটা সংরক্ষণের জন্য SharedPreferences ব্যবহার করেন, তাহলে DataStore-এ মাইগ্রেট করার কথা বিবেচনা করুন।

ডেটাস্টোর এপিআই

DataStore ইন্টারফেস নিম্নলিখিত API প্রদান করে:

  1. একটি প্রবাহ যা ডেটাস্টোর থেকে ডেটা পড়ার জন্য ব্যবহার করা যেতে পারে

    val data: Flow<T>
    
  2. ডেটাস্টোরে ডেটা আপডেট করার জন্য একটি ফাংশন

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

ডেটাস্টোর কনফিগারেশন

যদি আপনি কী ব্যবহার করে ডেটা সংরক্ষণ এবং অ্যাক্সেস করতে চান, তাহলে Preferences DataStore বাস্তবায়ন ব্যবহার করুন যার জন্য পূর্বনির্ধারিত স্কিমার প্রয়োজন হয় না এবং এটি টাইপ সুরক্ষা প্রদান করে না। এটির একটি SharedPreferences -এর মতো API আছে কিন্তু শেয়ার করা পছন্দের সাথে সম্পর্কিত ত্রুটিগুলি নেই।

ডেটাস্টোর আপনাকে কাস্টম ক্লাসগুলি ধরে রাখতে দেয়। এটি করার জন্য, আপনাকে ডেটার জন্য একটি স্কিমা সংজ্ঞায়িত করতে হবে এবং এটিকে একটি স্থায়ী বিন্যাসে রূপান্তর করার জন্য একটি Serializer সরবরাহ করতে হবে। আপনি প্রোটোকল বাফার, 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")
                }
            }
        }
    }
    

ডেটাস্টোর সঠিকভাবে ব্যবহার করুন

ডেটাস্টোর সঠিকভাবে ব্যবহার করার জন্য সর্বদা নিম্নলিখিত নিয়মগুলি মনে রাখবেন:

  1. একই প্রক্রিয়ায় একটি নির্দিষ্ট ফাইলের জন্য DataStore এর একাধিক উদাহরণ তৈরি করবেন না। এটি করলে DataStore-এর সমস্ত কার্যকারিতা ব্যাহত হতে পারে। একই প্রক্রিয়ায় যদি একটি নির্দিষ্ট ফাইলের জন্য একাধিক DataStores সক্রিয় থাকে, তাহলে DataStore ডেটা পড়ার বা আপডেট করার সময় IllegalStateException নিক্ষেপ করবে।

  2. DataStore<T> এর জেনেরিক টাইপ অবশ্যই অপরিবর্তনীয় হতে হবে। DataStore-এ ব্যবহৃত টাইপ পরিবর্তন করলে DataStore-এর প্রদত্ত ধারাবাহিকতা বাতিল হয়ে যায় এবং সম্ভাব্য গুরুতর, ধরা কঠিন বাগ তৈরি হয়। আমরা আপনাকে প্রোটোকল বাফার ব্যবহার করার পরামর্শ দিচ্ছি, যা অপরিবর্তনীয়তা, একটি স্পষ্ট API এবং দক্ষ সিরিয়ালাইজেশন নিশ্চিত করতে সহায়তা করে।

  3. একই ফাইলের জন্য SingleProcessDataStore এবং MultiProcessDataStore এর ব্যবহার একত্রিত করবেন না । যদি আপনি একাধিক প্রক্রিয়া থেকে DataStore অ্যাক্সেস করতে চান, তাহলে আপনাকে অবশ্যই MultiProcessDataStore ব্যবহার করতে হবে।

ডেটা সংজ্ঞা

পছন্দসমূহ ডেটাস্টোর

ডিস্কে ডেটা ধরে রাখার জন্য ব্যবহৃত হবে এমন একটি কী সংজ্ঞায়িত করুন।

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

প্রোটো ডেটাস্টোর

প্রোটো ডেটাস্টোর বাস্তবায়ন ডেটাস্টোর এবং প্রোটোকল বাফার ব্যবহার করে টাইপ করা বস্তুগুলিকে ডিস্কে ধরে রাখে।

প্রোটো ডেটাস্টোরের জন্য app/src/main/proto/ ডিরেক্টরির একটি প্রোটো ফাইলে একটি পূর্বনির্ধারিত স্কিমা প্রয়োজন। এই স্কিমা আপনার প্রোটো ডেটাস্টোরে থাকা বস্তুর ধরণ নির্ধারণ করে। প্রোটো স্কিমা সংজ্ঞায়িত করার বিষয়ে আরও জানতে, প্রোটোবফ ভাষা নির্দেশিকাটি দেখুন।

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 হল প্রোটো ফাইলে সংজ্ঞায়িত টাইপ। এই সিরিয়ালাইজার ক্লাসটি ডেটাস্টোর কীভাবে আপনার ডেটা টাইপ পড়ে এবং লেখে তা নির্ধারণ করে। যদি এখনও কোনও ফাইল তৈরি না করা হয় তবে সিরিয়ালাইজার ব্যবহারের জন্য একটি ডিফল্ট মান অন্তর্ভুক্ত করুন।

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 বাস্তবায়নে DataStore এবং Preferences ক্লাস ব্যবহার করে ডিস্কে কী-মান জোড়া ধরে রাখা হয়। PreferencesDataStore দ্বারা তৈরি প্রপার্টি ডেলিগেট ব্যবহার করে DataStore<Preferences> এর একটি ইনস্ট্যান্স তৈরি করুন। আপনার Kotlin ফাইলের উপরের স্তরে একবার এটি কল করুন। আপনার অ্যাপ্লিকেশনের বাকি অংশ জুড়ে এই প্রপার্টির মাধ্যমে DataStore অ্যাক্সেস করুন। এটি আপনার DataStore কে একটি সিঙ্গেলটন হিসেবে রাখা সহজ করে তোলে। বিকল্পভাবে, আপনি যদি RxJava ব্যবহার করেন তবে RxPreferenceDataStoreBuilder ব্যবহার করুন। বাধ্যতামূলক 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 হল serializable ডেটা ক্লাস। আপনার kotlin ফাইলের উপরের স্তরে একবার এটি কল করুন এবং আপনার অ্যাপের বাকি অংশ জুড়ে এই প্রপার্টি ডেলিগেটের মাধ্যমে এটি অ্যাক্সেস করুন। fileName প্যারামিটার DataStore কে বলে যে কোন ফাইলটি ডেটা সংরক্ষণ করতে হবে, এবং serializer প্যারামিটার DataStore কে বলে যে ধাপ 1 এ সংজ্ঞায়িত serializer ক্লাসের নাম।

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

প্রোটো ডেটাস্টোর

dataStore দ্বারা তৈরি প্রপার্টি ডেলিগেট ব্যবহার করে DataStore<T> এর একটি ইনস্ট্যান্স তৈরি করুন, যেখানে T হল প্রোটো ফাইলে সংজ্ঞায়িত টাইপ। আপনার Kotlin ফাইলের উপরের স্তরে একবার এটি কল করুন এবং আপনার অ্যাপের বাকি অংশ জুড়ে এই প্রপার্টি ডেলিগেটের মাধ্যমে এটি অ্যাক্সেস করুন। fileName প্যারামিটার DataStore কে ডেটা স্টোর করার জন্য কোন ফাইল ব্যবহার করতে হবে তা বলে দেয় এবং serializer প্যারামিটার DataStore কে ধাপ 1 এ সংজ্ঞায়িত serializer ক্লাসের নাম বলে।

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

ডেটাস্টোর থেকে পড়ুন

ডেটা ধরে রাখার জন্য ব্যবহৃত ফাইলের জন্য আপনাকে একটি নাম নির্দিষ্ট করতে হবে।

পছন্দসমূহ ডেটাস্টোর

যেহেতু Preferences DataStore পূর্বনির্ধারিত স্কিমা ব্যবহার করে না, তাই DataStore<Preferences> ইনস্ট্যান্সে আপনার প্রয়োজনীয় প্রতিটি মানের জন্য একটি কী সংজ্ঞায়িত করতে আপনাকে সংশ্লিষ্ট কী টাইপ ফাংশন ব্যবহার করতে হবে। উদাহরণস্বরূপ, একটি int মানের জন্য একটি কী সংজ্ঞায়িত করতে, intPreferencesKey() ব্যবহার করুন। তারপর, Flow ব্যবহার করে উপযুক্ত সঞ্চিত মান প্রকাশ করতে DataStore.data বৈশিষ্ট্য ব্যবহার করুন।

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
}

প্রোটো ডেটাস্টোর

আপনার সঞ্চিত বস্তু থেকে উপযুক্ত সম্পত্তির একটি Flow প্রকাশ করতে DataStore.data ব্যবহার করুন।

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

ডেটাস্টোরে লিখুন

DataStore একটি updateData() ফাংশন প্রদান করে যা লেনদেনের মাধ্যমে একটি সঞ্চিত বস্তু আপডেট করে। updateData আপনার ডেটা ধরণের একটি উদাহরণ হিসাবে ডেটার বর্তমান অবস্থা দেখায় এবং একটি পারমাণবিক পঠন-লেখা-পরিবর্তন অপারেশনে লেনদেনের মাধ্যমে ডেটা আপডেট করে। updateData ব্লকের সমস্ত কোডকে একটি একক লেনদেন হিসাবে বিবেচনা করা হয়।

পছন্দসমূহ ডেটাস্টোর

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

প্রোটো ডেটাস্টোর

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

নমুনা রচনা করুন

আপনি এই ফাংশনগুলিকে একটি ক্লাসে একত্রিত করতে পারেন এবং একটি কম্পোজ অ্যাপে ব্যবহার করতে পারেন।

পছন্দসমূহ ডেটাস্টোর

আমরা এখন এই ফাংশনগুলিকে 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 ডেটাস্টোর

আমরা এখন এই ফাংশনগুলিকে 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")
}

প্রোটো ডেটাস্টোর

আমরা এখন এই ফাংশনগুলিকে 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 এর প্রধান সুবিধাগুলির মধ্যে একটি হল অ্যাসিঙ্ক্রোনাস API, তবে আপনার চারপাশের কোডটিকে অ্যাসিঙ্ক্রোনাস করা সবসময় সম্ভব নাও হতে পারে। এটি এমন ক্ষেত্রে হতে পারে যদি আপনি এমন একটি বিদ্যমান কোডবেসের সাথে কাজ করেন যা সিঙ্ক্রোনাস ডিস্ক I/O ব্যবহার করে অথবা যদি আপনার এমন একটি নির্ভরতা থাকে যা অ্যাসিঙ্ক্রোনাস API প্রদান করে না।

কোটলিন কোরোটিনগুলি সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস কোডের মধ্যে ব্যবধান পূরণ করতে সাহায্য করার জন্য runBlocking() কোরোটিন বিল্ডার প্রদান করে। আপনি DataStore থেকে সিঙ্ক্রোনাসভাবে ডেটা পড়ার জন্য runBlocking() ব্যবহার করতে পারেন। RxJava Flowable এ ব্লকিং পদ্ধতি অফার করে। DataStore ডেটা ফেরত না দেওয়া পর্যন্ত নিম্নলিখিত কোড কলিং থ্রেডকে ব্লক করে:

কোটলিন

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

জাভা

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

UI থ্রেডে সিঙ্ক্রোনাস I/O অপারেশন করলে ANR বা প্রতিক্রিয়াহীন UI হতে পারে। DataStore থেকে ডেটা অ্যাসিঙ্ক্রোনাসভাবে প্রিলোড করে আপনি এই সমস্যাগুলি কমাতে পারেন:

কোটলিন

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

জাভা

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

এইভাবে, DataStore অ্যাসিঙ্ক্রোনাসভাবে ডেটা পড়ে এবং মেমোরিতে ক্যাশে করে। runBlocking() ব্যবহার করে পরবর্তীতে সিঙ্ক্রোনাস রিড দ্রুততর হতে পারে অথবা প্রাথমিক রিড সম্পন্ন হলে ডিস্ক I/O অপারেশন সম্পূর্ণরূপে এড়াতে পারে।

মাল্টি-প্রসেস কোডে ডেটাস্টোর ব্যবহার করুন

আপনি DataStore কে বিভিন্ন প্রক্রিয়ায় একই ডেটা অ্যাক্সেস করার জন্য কনফিগার করতে পারেন, যার ডেটা কনসিস্টেন্সি প্রোপার্টি একই, যা একটি একক প্রক্রিয়ার মধ্যে থেকে পাওয়া যায়। বিশেষ করে, DataStore নিম্নলিখিত সুবিধা প্রদান করে:

  • রিড শুধুমাত্র ডিস্কে সংরক্ষিত ডেটা ফেরত দেয়।
  • পড়ার পর লেখার ধারাবাহিকতা।
  • লেখাগুলো ধারাবাহিকভাবে সাজানো হয়েছে।
  • লেখার মাধ্যমে পঠন কখনও অবরুদ্ধ হয় না।

একটি পরিষেবা এবং একটি কার্যকলাপ সহ একটি নমুনা অ্যাপ্লিকেশন বিবেচনা করুন যেখানে পরিষেবাটি একটি পৃথক প্রক্রিয়ায় চলছে এবং পর্যায়ক্রমে ডেটাস্টোর আপডেট করে।

এই উদাহরণে একটি JSON ডেটাস্টোর ব্যবহার করা হয়েছে, তবে আপনি একটি পছন্দ বা প্রোটো ডেটাস্টোরও ব্যবহার করতে পারেন।

@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 ব্যবহার করতে সক্ষম হতে, আপনাকে অ্যাপ এবং পরিষেবা কোড উভয়ের জন্য MultiProcessDataStoreFactory ব্যবহার করে DataStore অবজেক্ট তৈরি করতে হবে:

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() কল করে, যা 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 dependence injection ব্যবহার করতে পারেন যাতে আপনার DataStore ইনস্ট্যান্স প্রতিটি প্রক্রিয়ার জন্য অনন্য হয়:

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

ফাইল দুর্নীতি পরিচালনা করুন

DataStore-এর স্থায়ী অন-ডিস্ক ফাইলটি দূষিত হওয়ার সম্ভাবনা খুব কমই থাকে। ডিফল্টরূপে, DataStore স্বয়ংক্রিয়ভাবে দূষিত অবস্থা থেকে পুনরুদ্ধার করে না এবং এটি থেকে পড়ার চেষ্টা করলে সিস্টেমটি CorruptionException নিক্ষেপ করবে।

ডেটাস্টোর একটি দুর্নীতি হ্যান্ডলার API অফার করে যা আপনাকে এমন পরিস্থিতিতে সুন্দরভাবে পুনরুদ্ধার করতে সাহায্য করতে পারে এবং ব্যতিক্রমটি এড়াতে পারে। কনফিগার করা হলে, দুর্নীতি হ্যান্ডলারটি একটি পূর্বনির্ধারিত ডিফল্ট মান ধারণকারী একটি নতুন ফাইল দিয়ে দূষিত ফাইলটি প্রতিস্থাপন করে।

এই হ্যান্ডলারটি সেট আপ করার জন্য, by dataStore() অথবা DataStoreFactory factory পদ্ধতিতে DataStore ইনস্ট্যান্স তৈরি করার সময় একটি corruptionHandler প্রদান করুন:

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

মতামত প্রদান করুন

এই রিসোর্সের মাধ্যমে আপনার মতামত এবং ধারণা আমাদের সাথে শেয়ার করুন:

ইস্যু ট্র্যাকার :
সমস্যাগুলি রিপোর্ট করুন যাতে আমরা বাগগুলি ঠিক করতে পারি।

অতিরিক্ত সম্পদ

জেটপ্যাক ডেটাস্টোর সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত সংস্থানগুলি দেখুন:

নমুনা

ব্লগ

কোডল্যাব

{% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %} {% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %}