DataStore হল Jetpack লাইব্রেরির একটি উপাদান যা Android অ্যাপ্লিকেশনে অল্প পরিমাণ ডেটা সংরক্ষণের জন্য ডিজাইন করা হয়েছে। SharedPreferences-এর বিপরীতে, এটি অ্যাসিঙ্ক্রোনাসভাবে কাজ করে এবং সমবর্তী অ্যাক্সেসের অধীনে ডেটার সামঞ্জস্য নিশ্চিত করে। Google, 2024-এর মতে, DataStore Kotlin Coroutines এবং Flow ব্যবহার করে, যা এটিকে প্রধান থ্রেডের জন্য নিরাপদ এবং রিঅ্যাকটিভ আর্কিটেকচারের জন্য উপযুক্ত করে তোলে।
মূল পয়েন্ট
DataStore হল Android-এ স্থানীয় ডেটা স্টোরেজের জন্য Google-এর সমাধান, যা 2020 সালে SharedPreferences-এর বিকল্প হিসেবে উপস্থাপিত হয়েছিল। এটি দুটি মোড সমর্থন করে: Preferences DataStore (সরল কী-ভ্যালু জোড়া) এবং Proto DataStore (Protocol Buffers-এর উপর ভিত্তি করে টাইপকৃত স্কিমা)।
প্রধান সুবিধা হল সম্পূর্ণ অ্যাসিঙ্ক্রোনাসিটি: সমস্ত রিড অপারেশন Kotlin Coroutines থেকে Flow রিটার্ন করে এবং রাইটগুলি coroutine-প্রসঙ্গে সম্পাদিত হয়। এটি প্রধান থ্রেড ব্লকিং দূর করে, যা বড় পরিমাণ ডেটা নিয়ে কাজ করার সময় SharedPreferences-এর একটি সাধারণ সমস্যা ছিল।
DataStore অপারেশনের পরমাণুতা নিশ্চিত করে: সমবর্তী রাইটগুলি তার ট্রানজেকশনাল মডেলের কারণে ডেটা ক্ষতি করে না। যদি দুটি উপাদান একই সাথে একই মান পরিবর্তন করে, DataStore compare-and-swap প্রক্রিয়ার মাধ্যমে সঠিকভাবে দ্বন্দ্ব পরিচালনা করে।
Google I/O 2023-এর মতে, DataStore 40% নতুন Android প্রকল্পে ব্যবহৃত হয়, এবং Google সেই সমস্ত অ্যাপ্লিকেশনে SharedPreferences থেকে মাইগ্রেট করার সুপারিশ করে যেখানে সেটিংস স্টোরেজ স্থিতিশীলতা প্রয়োজন।
DataStore-এর মূলে রয়েছে SingleProcessDataStore — একটি বাস্তবায়ন যা একটি প্রক্রিয়ার মধ্যে কাজ করে। এটি ফাইল-লকিং সহ ফাইল-ভিত্তিক স্টোরেজ ব্যবহার করে: ডেটা লেখার সময়, ফাইলটি লক হয়ে যায়, যা সমবর্তী অ্যাক্সেসের অধীনে দূষণ প্রতিরোধ করে।
DataStore স্বয়ংক্রিয়ভাবে ডিসিরিয়ালাইজেশন ত্রুটিগুলি পরিচালনা করে: ফাইলটি দূষিত হলে, এটি একটি ডিফল্ট মান ফেরত দেয় এবং ফাইলটি ওভাররাইট করে। এই আচরণ corruptionHandler-এর মাধ্যমে কনফিগারযোগ্য, যা DataStore তৈরি করার সময় সেট করা যেতে পারে।
SharedPreferences তিনটি মৌলিক সমস্যায় ভোগে: প্রধান থ্রেডে সিঙ্ক্রোনাস ডিস্ক রিডিং, সমবর্তী রাইটের জন্য পরমাণুতা গ্যারান্টির অভাব এবং রিঅ্যাকটিভভাবে পরিবর্তন ট্র্যাক করতে অক্ষমতা। DataStore তিনটিই সমাধান করে: পর্যবেক্ষণের জন্য Flow, পরমাণুতার জন্য ফাইল লকিং এবং থ্রেড নিরাপত্তার জন্য অ্যাসিঙ্ক্রোনাস API।
DataStore ডিভাইসের অভ্যন্তরীণ স্টোরেজে ফাইলে ডেটা সংরক্ষণ করে। Preferences DataStore SharedPreferences-এর মতো ফাইল ফরম্যাট ব্যবহার করে তবে অখণ্ডতা পরীক্ষার জন্য অতিরিক্ত মেটাডেটা সহ। Proto DataStore বাইনারি Protocol Buffers ফরম্যাট ব্যবহার করে, যা ফাইলের আকার হ্রাস করে এবং সিরিয়ালাইজেশন ত্বরান্বিত করে।
ডেটা পড়ার সময়, DataStore সম্পূর্ণ ফাইলটি একবার মেমরিতে লোড করে, তারপর সাবস্ক্রাইবাররা Flow-এর মাধ্যমে বর্তমান অবস্থা পায়। পরিবর্তনগুলি সমস্ত সক্রিয় সাবস্ক্রাইবারকে স্বয়ংক্রিয়ভাবে সম্প্রচারিত হয় — SharedPreferences-এর মতো ম্যানুয়াল listener নিবন্ধনের প্রয়োজন নেই।
Preferences DataStore একটি Map-এর উপর ভিত্তি করে একটি অন্তর্নির্মিত সিরিয়ালাইজেশন প্রক্রিয়া ব্যবহার করে। প্রতিটি এন্ট্রি একটি স্ট্রিং এবং একটি আদিম টাইপের (Int, Boolean, Float, Long, String, Set) জোড়া। ডেটা SharedPreferences-এর মতো XML ফাইলে সংরক্ষণ করা হয়, তবে ফাইল লকিংয়ের মাধ্যমে পরমাণু লেখার সাথে।
Preferences DataStore তৈরির উদাহরণ: Context-এ preferencesDataStore এক্সটেনশন ফাইল নামের সাথে একটি সিঙ্গেলটন তৈরি করে। বারবার কল করলে, একই ইনস্ট্যান্স ফেরত দেওয়া হয় — এটি ফাইল ডুপ্লিকেশন এবং বিভিন্ন স্টোরেজ ইনস্ট্যান্সের মধ্যে বিভ্রান্তি দূর করে।
Proto DataStore-এর জন্য .proto ফাইলের মাধ্যমে ডেটা স্কিমা সংজ্ঞায়িত করা এবং protobuf-প্লাগইনের সাথে কম্পাইল করা প্রয়োজন। জেনারেটেড Java ক্লাসটি সমস্ত ফিল্ডের জন্য একমাত্র এন্ট্রি পয়েন্ট হিসাবে ব্যবহৃত হয় — এটি SharedPreferences-এ সাধারণ কী টাইপো দূর করে।
Proto DataStore স্কিমা একবার সংজ্ঞায়িত করা হয় এবং পুরানো ডেটা না হারিয়ে নতুন ফিল্ড যোগ করার সমর্থন করে। যদি অ্যাপ্লিকেশনের নতুন সংস্করণ ডিফল্ট মান সহ একটি ফিল্ড যোগ করে, পুরানো ফাইলটি সঠিকভাবে ডিসিরিয়ালাইজ করা হবে — ব্যাকওয়ার্ড কম্প্যাটিবিলিটি প্রোটোকলে নির্মিত।
Preferences DataStore এবং Proto DataStore-এর মধ্যে পছন্দ ডেটা জটিলতা এবং টাইপিং প্রয়োজনীয়তার উপর নির্ভর করে। উভয় বিকল্প অ্যাসিঙ্ক্রোনাস এবং ট্রানজেকশনাল, তবে টাইপ-সেফটি এবং সিরিয়ালাইজেশন কর্মক্ষমতায় ভিন্ন।
| বৈশিষ্ট্য | Preferences DataStore | Proto DataStore |
|---|---|---|
| টাইপিং | দুর্বল (কী-ভ্যালু) | শক্তিশালী (জেনারেটেড ক্লাস) |
| সিরিয়ালাইজেশন | XML (অন্তর্নির্মিত) | Protocol Buffers (protobuf) |
| ফাইলের আকার | বড় (পাঠযোগ্য XML) | ছোট (বাইনারি) |
| জটিলতা | কম (.proto ছাড়া) | মাঝারি (.proto প্রয়োজন) |
| স্কিমা মাইগ্রেশন | কোনো স্কিমা নেই | স্বয়ংক্রিয় (proto) |
| সামঞ্জস্যতা | SharedPreferences (মাইগ্রেশনের মাধ্যমে) | শুধুমাত্র Proto DataStore |
Preferences DataStore সাধারণ সেটিংসের জন্য উপযুক্ত: ফিচার ফ্ল্যাগ, অথরাইজেশন টোকেন স্ট্রিং, অ্যাপ্লিকেশন লঞ্চ কাউন্ট। যদি ডেটা ছোট হয় (10–15 কী পর্যন্ত) এবং কঠোর স্কিমার প্রয়োজন না হয়, তাহলে Preferences DataStore protobuf-প্লাগইন সংযোগ ছাড়াই ন্যূনতম প্রবেশ সীমা প্রদান করে।
Proto DataStore ন্যায়সঙ্গত যখন ডেটা কাঠামো জটিল হয় বা অ্যাপ্লিকেশন সংস্করণগুলির মধ্যে পরিবর্তিত হতে পারে। উদাহরণস্বরূপ, ব্যবহারকারী প্রোফাইল সেটিংস বা 20+ ফিল্ড সহ A/B পরীক্ষা কনফিগারেশন। Protobuf শক্তিশালী টাইপিং এবং স্বয়ংক্রিয় মাইগ্রেশন প্রদান করে, কী অমিলের কারণে রানটাইম ত্রুটিগুলি দূর করে।
Google SharedPreferencesMigration ক্লাসের মাধ্যমে একটি অন্তর্নির্মিত মাইগ্রেশন প্রক্রিয়া প্রদান করে। অ্যাপ্লিকেশন আপডেটের পরে প্রথম লঞ্চে মাইগ্রেশন একবার সম্পাদিত হয়: DataStore SharedPreferences থেকে ডেটা পড়ে, এটি নিজস্ব ফরম্যাটে লেখে এবং মাইগ্রেশন সম্পূর্ণ হিসাবে চিহ্নিত করে।
মাইগ্রেশন কাস্টম ট্রান্সফরমেশন সমর্থন করে: যদি SharedPreferences-এর কীগুলি কাঙ্ক্ষিত DataStore কীগুলির সাথে মেলে না, তাহলে আপনি SharedPreferencesMigration-এর মাধ্যমে একটি ট্রান্সফরমেশন ফাংশন নির্দিষ্ট করতে পারেন। এটি মাইগ্রেশনের সময় কীগুলির নাম পরিবর্তন এবং ডেটা টাইপ পরিবর্তনের অনুমতি দেয়।
প্রথম ধাপে, build.gradle-এ DataStore যোগ করুন এবং মাইগ্রেশন সহ একটি DataStore ইনস্ট্যান্স তৈরি করুন: SharedPreferencesMigration SharedPreferences ফাইলের নাম এবং স্থানান্তরের জন্য কীগুলির সেট গ্রহণ করে। দ্বিতীয় ধাপে, SharedPreferences-এর মাধ্যমে কাজ করা সমস্ত কোড সরান এবং DataStore কল দিয়ে প্রতিস্থাপন করুন। তৃতীয় ধাপে, মাইগ্রেশন পরীক্ষা করুন: প্রথম লঞ্চে, DataStore-এ ডেটা উপস্থিত হওয়া উচিত এবং পুরানো SharedPreferences ফাইলটি আর ব্যবহার করা উচিত নয়।
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(
SharedPreferencesMigration(context, "old_prefs")
)
}
)
DataStore বিদ্যমান প্রকল্পে সহজেই একীভূত হয়। নীচে Preferences DataStore এবং Proto DataStore-এর জন্য ব্যবহারিক উদাহরণ দেওয়া হল — উভয়ই ডেটা পড়া, লেখা এবং রিঅ্যাকটিভ পর্যবেক্ষণ প্রদর্শন করে।
এই উদাহরণে, Preferences DataStore তিনটি সেটিংস সংরক্ষণ করে: ডার্ক থিম, ব্যবহারকারীর নাম এবং লঞ্চ কাউন্ট। রিডিং .data এক্সটেনশনের মাধ্যমে করা হয়, যা Flow রিটার্ন করে। রাইটিং .edit সাসপেন্ড ফাংশনের মাধ্যমে করা হয়, যা পরিবর্তনের পরমাণুতা নিশ্চিত করে।
val Context.settingsDataStore by preferencesDataStore(name = "settings")
val isDarkMode: Flow<Boolean> = settingsDataStore.data
.map { preferences ->
preferences[booleanPreferencesKey("dark_mode")] ?: false
}
suspend fun toggleDarkMode() {
settingsDataStore.edit { prefs ->
val current = prefs[booleanPreferencesKey("dark_mode")] ?: false
prefs[booleanPreferencesKey("dark_mode")] = !current
}
}
Proto DataStore-এর জন্য .proto ফাইল সংজ্ঞায়িত করা প্রয়োজন। কম্পাইলেশনের পরে, একটি UserSettings ক্লাস তৈরি হয় যা পড়া এবং লেখার জন্য ব্যবহৃত হয়। স্কিমার সংস্করণ মাইগ্রেশনগুলি একই .proto ফাইলে বর্ণিত হয় এবং স্বয়ংক্রিয়ভাবে প্রয়োগ হয়।
// user_preferences.proto
syntax = "proto3";
message UserPreferences {
string display_name = 1;
int32 notification_count = 2;
bool notifications_enabled = 3;
}
// DataStore থেকে পড়া
val userPreferencesFlow: Flow<UserPreferences> =
protoDataStore.data
// নতুন মান লেখা
suspend fun updateDisplayName(name: String) {
protoDataStore.updateData { prefs ->
prefs.toBuilder()
.setDisplayName(name)
.build()
}
}
DataStore ViewModel-এর মাধ্যমে MVVM আর্কিটেকচারের সাথে একীভূত হয়। DataStore থেকে Flow .stateIn-এর মাধ্যমে সংগ্রহ করা হয় এবং UI-তে ব্যবহৃত হয়। প্রতিটি ডেটা পরিবর্তনের সাথে, UI স্বয়ংক্রিয়ভাবে আপডেট হয় — ম্যানুয়াল আপডেট বা LiveData-এর প্রয়োজন নেই।
class SettingsViewModel(
private val dataStore: DataStore<Preferences>
) : ViewModel() {
val uiState: StateFlow<SettingsUiState> =
dataStore.data
.map { prefs ->
SettingsUiState(
isDarkMode = prefs[booleanPreferencesKey("dark_mode")] ?: false,
counter = prefs[intPreferencesKey("launch_count")] ?: 0
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = SettingsUiState()
)
}
প্রায়শই জিজ্ঞাসিত প্রশ্ন
DataStore অ্যাসিঙ্ক্রোনাসভাবে কাজ করে (UI থ্রেড ব্লক করে না), ট্রানজেকশনের মাধ্যমে সমবর্তী অ্যাক্সেস সমর্থন করে এবং Flow-এর মাধ্যমে পরিবর্তনের রিঅ্যাকটিভ সাবস্ক্রিপশনের অনুমতি দেয়। SharedPreferences একটি সিঙ্ক্রোনাস API যার বড় ডেটা ভলিউমে ANR-এর ঝুঁকি এবং কোনো অন্তর্নির্মিত রিঅ্যাকটিভিটি সমর্থন নেই।
DataStore Kotlin-এ লেখা এবং Kotlin Coroutines প্রয়োজন। Java থেকে এটি ব্যবহার করা সম্ভব তবে অসুবিধাজনক: আপনাকে CompletableFuture দিয়ে র্যাপার তৈরি করতে হবে বা ম্যানুয়ালি coroutines পরিচালনা করতে হবে। Java প্রকল্পের জন্য, Google SharedPreferences রাখার বা মডিউলে Kotlin যোগ করার সুপারিশ করে।
DataStore পড়ার সময় সম্পূর্ণ ফাইল মেমরিতে লোড করে, তাই এটি তালিকা বা বড় বস্তু সংরক্ষণের জন্য উপযুক্ত নয়। এই ধরনের পরিস্থিতির জন্য, Room বা SQLite ব্যবহার করুন। DataStore সেটিংস এবং ছোট কাঠামোবদ্ধ ডেটা — শত কিলোবাইট পর্যন্ত — এর জন্য অপ্টিমাইজ করা।
DataStore তৈরি করার সময়, আপনি একটি corruptionHandler পাস করতে পারেন — একটি ফাংশন যা ফাইল দূষিত হলে কল করা হয়। ডিফল্টরূপে, DataStore একটি CorruptionException নিক্ষেপ করে। corruptionHandler-এ, আপনি খালি ডেটা ফেরত দিতে পারেন, এরপর DataStore ফাইলটি সঠিক অবস্থায় ওভাররাইট করবে।
হ্যাঁ, Proto DataStore-এর জন্য .proto ফাইলে স্কিমা সংজ্ঞায়িত করা এবং protobuf-gradle-plugin সংযোগ করা প্রয়োজন। যদি প্রকল্প ছোট হয় এবং ডেটা সহজ হয়, তাহলে Preferences DataStore ব্যবহার করা সহজ — এটির অতিরিক্ত বিল্ড কনফিগারেশনের প্রয়োজন নেই।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন