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

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

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

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

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

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

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

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

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

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

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

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

To add optional RxJava support, add the following dependencies:

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

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

Add the following optional dependencies for RxJava support:

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

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 সঠিকভাবে ব্যবহার করার জন্য সর্বদা নিম্নলিখিত নিয়মগুলি মনে রাখবেন:

  1. একই প্রসেসে কোনো নির্দিষ্ট ফাইলের জন্য DataStore এর একাধিক ইনস্ট্যান্স তৈরি করবেন না। এমনটা করলে DataStore-এর সমস্ত কার্যকারিতা নষ্ট হয়ে যেতে পারে। যদি একই প্রসেসে কোনো নির্দিষ্ট ফাইলের জন্য একাধিক DataStore সক্রিয় থাকে, তাহলে ডেটা পড়া বা আপডেট করার সময় 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()
        )
    }
}

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

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

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

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

একটি ডেটাস্টোর তৈরি করুন

ডেটা সংরক্ষণের জন্য ব্যবহৃত ফাইলটির একটি নাম নির্দিষ্ট করতে হবে।

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

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 হলো সিরিয়ালাইজেবল ডেটা ক্লাস। আপনার কোটলিন ফাইলের টপ লেভেলে এটিকে একবার কল করুন এবং আপনার অ্যাপের বাকি অংশে এই প্রপার্টি ডেলিগেটের মাধ্যমে এটি অ্যাক্সেস করুন। fileName প্যারামিটারটি DataStore-কে বলে দেয় ডেটা সংরক্ষণের জন্য কোন ফাইলটি ব্যবহার করতে হবে, এবং serializer প্যারামিটারটি DataStore-কে পূর্বে সংজ্ঞায়িত সিরিয়ালাইজার ক্লাসের নাম বলে দেয়।

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

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

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

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

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

ডেটা সংরক্ষণের জন্য ব্যবহৃত ফাইলটির একটি নাম নির্দিষ্ট করতে হবে।

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

যেহেতু প্রেফারেন্সেস ডেটাস্টোর কোনো পূর্বনির্ধারিত স্কিমা ব্যবহার করে না, তাই DataStore<Preferences> ইনস্ট্যান্সে সংরক্ষণ করার জন্য প্রয়োজনীয় প্রতিটি মানের জন্য একটি কী (key) নির্ধারণ করতে আপনাকে অবশ্যই সংশ্লিষ্ট কী টাইপ ফাংশন ব্যবহার করতে হবে। উদাহরণস্বরূপ, একটি 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
}

একটি কম্পোজেবল-এর মধ্যে থাকা ViewModel দ্বারা উৎপাদিত Flow ব্যবহার করতে collectAsStateWithLifecycle ব্যবহার করুন। এটি নিরাপদে ডেটাস্টোর ফ্লো-কে কম্পোজ স্টেটে রূপান্তরিত করে, যা রিকম্পোজিশন ট্রিগার করে।

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

collectAsStateWithLifecycle সম্পর্কে আরও তথ্যের জন্য, State এবং Jetpack Compose দেখুন।

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

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

একটি কম্পোজ অ্যাপে ডেটাস্টোর ব্যবহার করুন

একটি Compose অ্যাপে DataStore ব্যবহার করতে, Android অ্যাপ আর্কিটেকচারের নির্দেশিকা অনুসরণ করুন। এর জন্য DataStore অপারেশনগুলিকে আপনার ডেটা লেয়ারে (যেমন একটি রিপোজিটরি) রাখুন এবং একটি ViewModel মাধ্যমে আপনার UI-তে ডেটা প্রকাশ করুন।

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

  1. একটি ViewModel-এর মাধ্যমে DataStore-কে উন্মুক্ত করুন। আপনার রিপোজিটরি (যা 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. আপনার কম্পোজেবল থেকে পর্যবেক্ষণ করুন এবং লিখুন। আপনার UI-তে 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 নিম্নলিখিত বৈশিষ্ট্যগুলি প্রদান করে:

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

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

এই উদাহরণে একটি 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.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(...)

ফাইলের দুর্নীতি মোকাবেলা করুন

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

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

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

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

মতামত দিন

এই মাধ্যমগুলোর মাধ্যমে আমাদের সাথে আপনার মতামত ও ধারণা শেয়ার করুন:

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

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

Jetpack DataStore সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত রিসোর্সগুলো দেখুন:

নমুনা

ব্লগ

কোডল্যাবস

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}