ViewModel হল একটি Android Jetpack Architecture উপাদান যা Activity এবং Fragment-এর জীবনচক্র বিবেচনায় রেখে UI ডেটা সংরক্ষণ এবং পরিচালনার জন্য ডিজাইন করা হয়েছে। Google I/O 2025-এর মতে, Jetpack-এর উপর নির্মিত 82% আধুনিক Android অ্যাপ্লিকেশনে ViewModel ব্যবহৃত হয়। সাধারণ ক্লাসের বিপরীতে, ViewModel স্বয়ংক্রিয়ভাবে স্ক্রিন রোটেশন এবং অন্যান্য কনফিগারেশন পরিবর্তন থেকে রক্ষা পায়, ডেটা হারানো ছাড়াই UI অবস্থা বজায় রাখে। MVVM (Model-View-ViewModel) আর্কিটেকচার ViewModel-এর উপর একটি কেন্দ্রীয় স্তর হিসাবে নির্ভর করে যা ব্যবসায়িক লজিককে ইন্টারফেসের সাথে সংযুক্ত করে।
মূল বিষয়
ViewModel হল Android Jetpack লাইব্রেরির একটি ক্লাস যা ব্যবহারকারী ইন্টারফেস সম্পর্কিত ডেটা সংরক্ষণ এবং পরিচালনার জন্য ডিজাইন করা হয়েছে, Activity বা Fragment-এর জীবনচক্র বিবেচনায় রেখে। ViewModel-এর প্রধান কাজ হল ডেটা প্রস্তুতির লজিককে UI স্তর থেকে পৃথক করা এবং কনফিগারেশন পরিবর্তনের সময় যেমন স্ক্রিন রোটেশন, থিম পরিবর্তন বা লোকেল পরিবর্তনের সময় এই ডেটা সংরক্ষণ করা।
ViewModel আসার আগে, ডেভেলপাররা UI অবস্থা সরাসরি Activity বা Fragment-এ সংরক্ষণ করতেন। স্ক্রিন ঘুরালে, Android Activity ধ্বংস করে এবং একটি নতুন তৈরি করে — সমস্ত অসংরক্ষিত ডেটা হারিয়ে যেত। সমাধান ছিল onSaveInstanceState()-এর মাধ্যমে অবস্থা সংরক্ষণ বা onRetainNonConfigurationInstance() ব্যবহার করা, কিন্তু উভয় পদ্ধতিতে ম্যানুয়াল ব্যবস্থাপনা এবং সিরিয়ালাইজেশন প্রয়োজন ছিল এবং জটিল অবজেক্টের জন্য উপযুক্ত ছিল না। ViewModel এই সমস্যাটি ফ্রেমওয়ার্ক স্তরে সমাধান করে: ডেটা UI থেকে আলাদাভাবে মেমরিতে থাকে এবং Activity পুনর্নির্মাণের সময় স্বয়ংক্রিয়ভাবে ফিরে আসে।
Android Developers ডকুমেন্টেশন (2025) অনুসারে, ViewModel প্রক্রিয়ার RAM-এ ডেটা সংরক্ষণ করে — এটি onSaveInstanceState()-এর মাধ্যমে Bundle থেকে পুনরুদ্ধারের চেয়ে 10–50 গুণ দ্রুত, যেখানে বাইট অ্যারেতে সিরিয়ালাইজেশন প্রয়োজন। ViewModel সেই সমস্ত স্ক্রিনের জন্য সুপারিশ করা হয় যেখানে ডেটা সরল প্রিমিটিভ বা স্ট্রিং-এর চেয়ে জটিল।
ViewModel জীবনচক্র মৌলিকভাবে Activity জীবনচক্র থেকে আলাদা: ViewModel স্ক্রিন রোটেশনে ধ্বংস হয় না এবং স্কোপ সম্পূর্ণরূপে শেষ না হওয়া পর্যন্ত বেঁচে থাকে (Activity.finish() বা Fragment সরানো হয়েছে)। এর অর্থ হল ViewModel-এ লোড করা যেকোনো ডেটা নেটওয়ার্ক বা ডেটাবেস থেকে পুনরায় লোড না করেই কনফিগারেশন পরিবর্তনের সময় উপলব্ধ থাকে।
Activity তৈরির সময়, সিস্টেম ViewModelProvider-এর মাধ্যমে ViewModel বরাদ্দ করে। ViewModelProvider.get(ViewModel::class.java)-এর প্রথম কলেই একটি নতুন ViewModel ইনস্ট্যান্স তৈরি হয়। পরবর্তী কলগুলিতে (রোটেশনের পরেও) একই ইনস্ট্যান্স ফিরে আসে। ViewModel পরিষ্কার করা স্বয়ংক্রিয়ভাবে onCleared() কল করার সময় ঘটে — এই পদ্ধতিটি তখনই কল হয় যখন Activity শেষ (finish()) হয় বা Fragment সম্পূর্ণরূপে সরানো হয়। ডেভেলপার onCleared() ওভাররাইড করতে পারেন সম্পদ মুক্ত করার জন্য: Flow থেকে আনসাবস্ক্রাইব করা, করুটিন বাতিল করা, সকেট বন্ধ করা।
Google Jetpack ডকুমেন্টেশনে জোর দেয়: ViewModel-এর ভিতরে কখনও Activity বা View-এর রেফারেন্স সংরক্ষণ করবেন না — এটি মেমরি লিকের কারণ কারণ ViewModel তার UI সহ Activity-কে超越 করে বেঁচে থাকে। পরিবর্তে, ViewModel এবং UI-এর মধ্যে ডেটা স্থানান্তরের জন্য LiveData, StateFlow বা SavedStateHandle ব্যবহার করুন।
MVVM (Model-View-ViewModel) প্যাটার্নে, ViewModel View (Activity/Fragment) এবং Model (রিপোজিটরি, DB, API)-এর মধ্যে একটি কেন্দ্রীয় স্থান দখল করে। View ViewModel (LiveData, StateFlow)-এর রিয়েক্টিভ ডেটায় সাবস্ক্রাইব করে এবং সেগুলি পরিবর্তন হলে স্বয়ংক্রিয়ভাবে আপডেট হয়। ViewModel View-এর অস্তিত্ব সম্পর্কে জানে না — এটি কেবল ডেটা এবং কমান্ড সরবরাহ করে, এবং View সিদ্ধান্ত নেয় কীভাবে সেগুলি প্রদর্শন করতে হবে।
MVP এবং MVVM-এর তুলনা: MVP-তে, প্রেজেন্টার সরাসরি View (ইন্টারফেস) পদ্ধতি কল করে, শক্ত সংযোগ তৈরি করে। MVVM-এ, ViewModel রিয়েক্টিভ ডেটা স্ট্রিম প্রকাশ করে এবং View সেগুলিতে সাবস্ক্রাইব করে — সংযোগটি একমুখী এবং পরীক্ষাযোগ্য। JetBrains Developer Survey (2024) অনুসারে, 68% Android ডেভেলপার MVVM-কে তাদের প্রাথমিক আর্কিটেকচার হিসাবে ব্যবহার করেন, এবং ViewModel এই প্যাটার্নের একটি মূল উপাদান।
IT Sectr-এ, আমরা 2018 সাল থেকে সমস্ত বাণিজ্যিক Kotlin প্রকল্পে ViewModel-সহ MVVM ব্যবহার করছি। অনুশীলন দেখায় যে এই পদ্ধতি দায়িত্বের স্পষ্ট বিভাজন এবং এমুলেটর ছাড়াই ব্যবসায়িক লজিকের পরীক্ষণযোগ্যতার কারণে UI লজিক ডিবাগিং সময় 30–40% কমিয়ে দেয়।
ViewModelProvider Fragment বা Activity-তে ViewModel পাওয়ার মানক উপায়। ডিফল্টরূপে, ViewModelProvider খালি কন্সট্রাক্টরের (কোনো আর্গুমেন্ট ছাড়া) মাধ্যমে ViewModel তৈরি করে। যদি ViewModel-এর প্যারামিটার প্রয়োজন হয় (যেমন রিপোজিটরি বা অ্যাপ্লিকেশন কনটেক্সট), তাহলে ViewModelProvider.Factory প্রয়োগ করা প্রয়োজন।
class UserViewModel(
private val userId: String,
private val repository: UserRepository
) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> get() = _user
fun loadUser() {
viewModelScope.launch {
_user.value = repository.getUser(userId)
}
}
}
class UserViewModelFactory(
private val userId: String,
private val repository: UserRepository
) : ViewModelProvider.Factory {
override fun create<T : ViewModel>(modelClass: Class<T>): T {
return UserViewModel(userId, repository) as T
}
}
Fragment বা Activity থেকে ViewModel পাওয়ার সময় কারখানাটি ViewModelProvider-এ দেওয়া হয়। SavedStateHandle হল AndroidX 1.2.0-তে প্রবর্তিত একটি বিকল্প প্যারামিটার পাসিং প্রক্রিয়া: ViewModel স্বয়ংক্রিয়ভাবে কন্সট্রাক্টরের মাধ্যমে SavedStateHandle গ্রহণ করে এবং কাস্টম কারখানা না লিখেই Bundle-এর মাধ্যমে আর্গুমেন্ট পাস করা হয়।
viewModelScope হল ViewModel-এ নির্মিত এবং তার জীবনচক্রের সাথে আবদ্ধ CoroutineScope। viewModelScope-তে লঞ্চ করা সমস্ত করুটিন onCleared() কল করার সময় স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়, যা ViewModel ধ্বংসের পরে মেমরি লিক এবং ব্যাকগ্রাউন্ড অপারেশন প্রতিরোধ করে।
class DashboardViewModel : ViewModel() {
private val _items = MutableLiveData<List<Item>>()
val items: LiveData<List<Item>> get() = _items
fun loadDashboard() {
viewModelScope.launch(Dispatchers.IO) {
val result = repository.fetchDashboard()
withContext(Dispatchers.Main) {
_items.value = result
}
}
}
override fun onCleared() {
super.onCleared()
// সমস্ত viewModelScope করুটিন স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়
}
}
viewModelScope-তে করুটিন ডিফল্টরূপে Dispatchers.Main-এ চলে। নেটওয়ার্ক বা ডিস্ক অপারেশনের জন্য, withContext ব্যবহার করে Dispatchers.IO-তে স্যুইচ করুন বা launch-এ ডিসপ্যাচার নির্দিষ্ট করুন। Google (Android Dev Summit 2024) অনুসারে, ম্যানুয়াল Job ব্যবস্থাপনার তুলনায় viewModelScope ব্যবহার করুটিন-সম্পর্কিত মেমরি লিক 95% কমিয়ে দেয়।
Hilt হল Dagger-এর উপর নির্মিত Android-এর জন্য Google-এর অফিসিয়াল ডিপেন্ডেন্সি ইনজেকশন লাইব্রেরি। Hilt-এর সাথে, ViewModelProvider.Factory ম্যানুয়ালি লেখার প্রয়োজন নেই — শুধু ViewModel কন্সট্রাক্টরকে @HiltViewModel অ্যানোটেশন দিয়ে চিহ্নিত করুন। Hilt স্বয়ংক্রিয়ভাবে কারখানা তৈরি করে এবং কন্সট্রাক্টরে ঘোষিত ডিপেন্ডেন্সি ইনজেক্ট করে।
@HiltViewModel
class ProfileViewModel constructor(
private val repository: UserRepository,
private val analytics: AnalyticsTracker
) : ViewModel() {
private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
val profile: StateFlow<ProfileState> get() = _profile
fun loadProfile(userId: String) {
viewModelScope.launch {
_profile.value = ProfileState.Success(repository.getUser(userId))
analytics.logEvent("profile_loaded")
}
}
}
// Fragment-এ — কারখানা ছাড়া:
val viewModel: ProfileViewModel = by viewModels()
Koin কোড জেনারেশন ছাড়া একটি বিকল্প DI লাইব্রেরি। Koin-এ, ViewModel viewModel { }-এর মাধ্যমে মডিউলে ঘোষণা করা হয় এবং Fragment-এ by viewModel()-এর মাধ্যমে পাওয়া যায়। Hilt এবং Koin-এর মধ্যে পছন্দ প্রকল্পের উপর নির্ভর করে: Hilt কম্পাইল সময়ে ডিপেন্ডেন্সি গ্রাফ যাচাইকরণ প্রদান করে, Koin হালকা এবং kapt/ksp প্রয়োজন হয় না। IT Sectr-এ, আমরা বড় প্রকল্পে (50+ স্ক্রিন) Hilt এবং মাঝারি প্রকল্পে Koin ব্যবহার করি।
একটি সরল ViewModel যা একটি পূর্ণসংখ্যা কাউন্টার সংরক্ষণ করে যা স্ক্রিন রোটেশনে রিসেট হয় না। MutableLiveData এবং LiveData ব্যবহারের মৌলিক প্যাটার্ন প্রদর্শন করে।
class CounterViewModel : ViewModel() {
private val _count = MutableLiveData(0)
val count: LiveData<Int> get() = _count
fun increment() {
_count.value = (_count.value ?: 0) + 1
}
fun reset() {
_count.value = 0
}
}
ViewModel যা সিস্টেম দ্বারা প্রক্রিয়া শেষ হলেও স্বয়ংক্রিয়ভাবে অবস্থা সংরক্ষণের জন্য SavedStateHandle ব্যবহার করে। SavedStateHandle হল একমাত্র প্রক্রিয়া যা অ্যাপকে ব্যাকগ্রাউন্ডে মিনিমাইজ এবং terminate করার সময় ডেটা সেভ করে।
class FormViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val userName = savedStateHandle.getLiveData<String>("userName", "")
val email = savedStateHandle.getLiveData<String>("email", "")
fun saveName(name: String) {
savedStateHandle["userName"] = name
}
fun saveEmail(email: String) {
savedStateHandle["email"] = email
}
}
SavedStateHandle থেকে LiveData স্বয়ংক্রিয়ভাবে শেষ মান Bundle-এ সংরক্ষণ করে। প্রক্রিয়া পুনর্নির্মাণের সময় (যেমন, অ্যাপ মিনিমাইজ এবং বন্ধ করার পরে), Bundle পুনরুদ্ধার হয় এবং LiveData পূর্ববর্তী মান গ্রহণ করে। Google পরীক্ষা অনুসারে, SavedStateHandle Bundle-এ 5 KB পর্যন্ত ডেটা সংরক্ষণের নিশ্চয়তা দেয় — টেক্সট ফিল্ড, ID এবং সিরিয়ালাইজড JSON অবজেক্টের জন্য যথেষ্ট।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
ViewModel প্রক্রিয়ার RAM-এ ডেটা সংরক্ষণ করে — এটি সিরিয়ালাইজেশন ছাড়াই তাত্ক্ষণিকভাবে উপলব্ধ, জটিল অবজেক্টের (তালিকা, Bitmap, নেটওয়ার্ক প্রতিক্রিয়া) জন্য উপযুক্ত। onSaveInstanceState() ডেটা Bundle-এ সিরিয়ালাইজ করে (Android 12 থেকে প্রতি লেনদেন সর্বোচ্চ 1 MB) এবং শুধুমাত্র সরল প্রিমিটিভ, String এবং Serializable/Parcelable-এর জন্য উপযুক্ত। ViewModel + SavedStateHandle হল Google-প্রস্তাবিত সংমিশ্রণ: রানটাইম ডেটার জন্য ViewModel, প্রক্রিয়া terminate হলে পুনরুদ্ধারের জন্য SavedStateHandle।
না, সিস্টেম স্বয়ংক্রিয়ভাবে স্কোপ শেষ হলে onCleared() কল করে। viewModelStore.clear()-এর মাধ্যমে ম্যানুয়াল পরিষ্কার শুধুমাত্র পরীক্ষার ক্ষেত্রে পরীক্ষার কেসগুলির মধ্যে লিক প্রতিরোধের জন্য প্রয়োজন। প্রোডাকশন কোডে, কখনও ম্যানুয়ালি clear() কল করবেন না — এটি ViewModel জীবনচক্র ভঙ্গ করে এবং অপ্রত্যাশিত UI আচরণের কারণ হতে পারে।
হ্যাঁ, ViewModel Jetpack Compose-এ viewModel() ফাংশনের মাধ্যমে সম্পূর্ণ সমর্থিত। Compose-এ, ViewModel Composable স্কোপ স্তরে পাওয়া যায় এবং স্কোপ থেকে বের হলে স্বয়ংক্রিয়ভাবে পরিষ্কার হয়। MVVM-এর Compose সংস্করণকে Unidirectional Data Flow (UDF) বলা হয়: ViewModel StateFlow প্রকাশ করে এবং Composable ফাংশন collectAsState()-এর মাধ্যমে সাবস্ক্রাইব করে। রিডিউসার পদ্ধতির Compose রূপ হল ViewModel-সহ MVI।
Activity, Fragment, View, Context (Application ব্যতীত) এর রেফারেন্স সংরক্ষণ করা নিষিদ্ধ। এটি মেমরি লিকের কারণ কারণ ViewModel UI কনটেক্সটকে超越 করে বেঁচে থাকে। সিরিয়ালাইজড View অবস্থা (যেমন RecyclerView অবস্থান) সংরক্ষণ করবেন না — LayoutManager.onSaveInstanceState() ব্যবহার করুন। বড় পরিমাণ ডেটা (10 MB-এর বেশি) সংরক্ষণ এড়িয়ে চলুন — প্রক্রিয়া মিনিমাইজ করলে SavedStateHandle ছাড়া ডেটা হারিয়ে যাবে।
ViewModel এমুলেটর ছাড়া সাধারণ Kotlin ক্লাসের মতো পরীক্ষা করা হয়: ইনস্ট্যান্স তৈরি করুন, পদ্ধতি কল করুন, LiveData বা StateFlow-এর অবস্থা পরীক্ষা করুন। করুটিন পরীক্ষার জন্য, TestDispatcher-সহ kotlinx-coroutines-test থেকে runTest ব্যবহার করুন। Hilt-সহ ViewModel-এর জন্য, পরীক্ষা Fragment-এ @HiltViewModelTest এবং hiltViewModel() ব্যবহার করুন। Google-এর মতে, ইউনিট পরীক্ষাগুলি ইন্সট্রুমেন্টেড পরীক্ষা ছাড়াই ViewModel লজিকের 80–90% কভার করে।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন