ভিউমডেলের সংক্ষিপ্ত বিবরণ। অ্যান্ড্রয়েড জেটপ্যাকের একটি অংশ।
ViewModel ক্লাসটি হলো একটি বিজনেস লজিক বা স্ক্রিন লেভেল স্টেট হোল্ডার । এটি UI-এর কাছে স্টেট প্রকাশ করে এবং সম্পর্কিত বিজনেস লজিককে এনক্যাপসুলেট করে। এর প্রধান সুবিধা হলো এটি স্টেট ক্যাশ করে রাখে এবং কনফিগারেশন পরিবর্তনের পরেও তা বজায় রাখে। এর মানে হলো, অ্যাক্টিভিটিগুলোর মধ্যে নেভিগেট করার সময়, বা স্ক্রিন ঘোরানোর মতো কনফিগারেশন পরিবর্তনের পরে আপনার UI-কে আবার ডেটা ফেচ করতে হয় না।
স্টেট হোল্ডারদের সম্পর্কে আরও তথ্যের জন্য, স্টেট হোল্ডারদের নির্দেশিকা দেখুন। একইভাবে, সাধারণভাবে UI লেয়ার সম্পর্কে আরও তথ্যের জন্য, UI লেয়ার নির্দেশিকা দেখুন।
ভিউমডেল সুবিধাগুলি
ViewModel-এর বিকল্প হলো একটি সাধারণ ক্লাস, যা আপনার UI-তে প্রদর্শিত ডেটা ধারণ করে। অ্যাক্টিভিটি বা নেভিগেশন গন্তব্যের মধ্যে নেভিগেট করার সময় এটি একটি সমস্যা হয়ে দাঁড়াতে পারে। আপনি যদি সেভড ইনস্ট্যান্স স্টেট মেকানিজম ব্যবহার করে ডেটা সংরক্ষণ না করেন, তবে এমনটা করলে সেই ডেটা নষ্ট হয়ে যায়। ViewModel ডেটা পার্সিস্টেন্সের জন্য একটি সুবিধাজনক API প্রদান করে যা এই সমস্যার সমাধান করে।
বিকল্পভাবে, শুধুমাত্র স্টেট হোল্ডারদের জন্য, Compose-এ retain ক্যাপাবিলিটি রয়েছে যা একটি ViewModel-এর সম্পূর্ণ পরিকাঠামো ছাড়াই সাধারণ ক্লাসগুলোকে কনফিগারেশন পরিবর্তন থেকে রক্ষা করতে দেয়। যদিও উভয় পদ্ধতিই স্টেট ধরে রাখতে সাহায্য করে, তবে সাধারণত একটি রিটেইনড ইনস্ট্যান্সে ViewModel প্রদান করা বেশি নিরাপদ, কারণ তাদের লাইফসাইকেল এবং ক্লিনআপ আচরণ ভিন্ন।
ViewModel ক্লাসের মূল সুবিধা মূলত দুটি:
- এটি আপনাকে UI স্টেট সংরক্ষণ করতে দেয়।
- এটি ব্যবসায়িক যুক্তিতে প্রবেশাধিকার প্রদান করে।
অধ্যবসায়
ViewModel তার নিজস্ব স্টেট এবং তার দ্বারা সম্পাদিত অপারেশন—উভয়ের মাধ্যমেই ডেটা সংরক্ষণ করতে দেয়। এই ক্যাশিংয়ের ফলে স্ক্রিন ঘোরানোর মতো সাধারণ কনফিগারেশন পরিবর্তনের সময় আপনাকে আর ডেটা পুনরায় ফেচ করতে হয় না।
পরিধি
যখন আপনি একটি ViewModel ইনস্ট্যানশিয়েট করেন, তখন আপনি এটিকে এমন একটি অবজেক্ট পাস করেন যা ViewModelStoreOwner ইন্টারফেসটি ইমপ্লিমেন্ট করে। এটি একটি নেভিগেশন ডেস্টিনেশন, নেভিগেশন গ্রাফ, অ্যাক্টিভিটি, বা ইন্টারফেসটি ইমপ্লিমেন্ট করে এমন অন্য যেকোনো টাইপ হতে পারে। আপনি rememberViewModelStoreOwner API ব্যবহার করে একটি ViewModel-কে সরাসরি একটি কম্পোজেবলের স্কোপেও আনতে পারেন। সেক্ষেত্রে আপনার ViewModel-টি ViewModelStoreOwner এর লাইফসাইকেলের স্কোপে থাকে। এটি মেমরিতে ততক্ষণ পর্যন্ত থাকে যতক্ষণ না এর ViewModelStoreOwner স্থায়ীভাবে চলে যায় (যেমন যখন কম্পোজেবল ওনার কম্পোজিশন থেকে বেরিয়ে যায়)।
বিভিন্ন ক্লাস ViewModelStoreOwner ইন্টারফেসের প্রত্যক্ষ বা পরোক্ষ সাবক্লাস। প্রত্যক্ষ সাবক্লাসগুলো হলো ComponentActivity এবং NavBackStackEntry । পরোক্ষ সাবক্লাসগুলোর সম্পূর্ণ তালিকার জন্য ViewModelStoreOwner রেফারেন্স দেখুন। একটি LazyList বা Pager এর স্বতন্ত্র আইটেমগুলোতে ViewModel-কে সীমাবদ্ধ করতে, মালিকানা ব্যবস্থাপনাকে প্যারেন্টে হোইস্ট করার জন্য rememberViewModelStoreProvider() ব্যবহার করুন।
যখন হোস্ট অ্যাক্টিভিটির কনফিগারেশনে কোনো পরিবর্তন আসে, তখন ViewModel-এ অ্যাসিঙ্ক্রোনাস কাজ চলতে থাকে, তা অ্যাক্টিভিটির স্কোপে থাকুক বা কোনো নির্দিষ্ট কম্পোজেবলের স্কোপে থাকুক। এটাই হলো ডেটা পার্সিস্টেন্সের মূল চাবিকাঠি।
আরও তথ্যের জন্য, পরবর্তী ViewModel লাইফসাইকেল বিভাগ, ViewModel স্কোপিং এপিআই (ViewModel Scoping APIs) , এবং Jetpack Compose-এর জন্য স্টেট হোস্টিং (state hoisting ) সংক্রান্ত নির্দেশিকা দেখুন।
সংরক্ষিত অবস্থা হ্যান্ডেল
SavedStateHandle আপনাকে শুধু কনফিগারেশন পরিবর্তনের মাধ্যমেই নয়, বরং প্রসেস বন্ধ হয়ে যাওয়ার পরেও ডেটা সংরক্ষণ করতে দেয়। অর্থাৎ, ব্যবহারকারী অ্যাপটি বন্ধ করে পরে আবার খুললেও এটি UI-এর অবস্থা অক্ষত রাখতে সাহায্য করে।
UI স্টেট সংরক্ষণ সম্পর্কে আরও তথ্যের জন্য, Compose-এ UI স্টেট সংরক্ষণ দেখুন।
ব্যবসায়িক যুক্তিতে প্রবেশাধিকার
যদিও ব্যবসায়িক যুক্তির সিংহভাগই ডেটা লেয়ারে থাকে, UI লেয়ারেও ব্যবসায়িক যুক্তি থাকতে পারে। একাধিক রিপোজিটরি থেকে ডেটা একত্রিত করে স্ক্রিনের UI স্টেট তৈরি করার ক্ষেত্রে, অথবা যখন কোনো নির্দিষ্ট ধরনের ডেটার জন্য ডেটা লেয়ারের প্রয়োজন হয় না, তখন এমনটা হতে পারে।
UI লেয়ারে বিজনেস লজিক পরিচালনার জন্য ViewModel হলো সঠিক জায়গা। এছাড়াও, অ্যাপ্লিকেশন ডেটা পরিবর্তন করার জন্য যখন বিজনেস লজিক প্রয়োগ করার প্রয়োজন হয়, তখন ইভেন্টগুলো পরিচালনা করা এবং সেগুলোকে হায়ারার্কির অন্যান্য লেয়ারে প্রেরণ করার দায়িত্বও ViewModel-এর উপরই থাকে।
একটি ভিউমডেল বাস্তবায়ন করুন
নিম্নলিখিতটি এমন একটি স্ক্রিনের জন্য ViewModel-এর একটি উদাহরণ বাস্তবায়ন, যা ব্যবহারকারীকে পাশা নিক্ষেপ করার সুযোগ দেয়।
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
// Expose screen UI state
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Handle business logic
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
এরপর আপনি একটি স্ক্রিন-লেভেল কম্পোজেবল থেকে নিম্নলিখিতভাবে ViewModel অ্যাক্সেস করতে পারবেন:
import androidx.lifecycle.viewmodel.compose.viewModel
// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
viewModel: DiceRollViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// Update UI elements
}
ViewModel-এর সাথে কো-রুটিন ব্যবহার করুন
ViewModel Kotlin coroutine-এর সাপোর্ট রয়েছে। এটি UI স্টেট সংরক্ষণের মতোই অ্যাসিঙ্ক্রোনাস কাজও সংরক্ষণ করতে সক্ষম।
আরও তথ্যের জন্য, Android Architecture Components-এর সাথে Kotlin coroutines-এর ব্যবহার দেখুন।
একটি ViewModel-এর জীবনচক্র
একটি ViewModel এর জীবনচক্র সরাসরি তার স্কোপের সাথে যুক্ত। একটি ViewModel ততক্ষণ পর্যন্ত মেমরিতে থাকে, যতক্ষণ না তার স্কোপভুক্ত ViewModelStoreOwner অদৃশ্য হয়ে যায়। এটি নিম্নলিখিত প্রেক্ষাপটে ঘটতে পারে:
- কোনো কার্যকলাপের ক্ষেত্রে, যখন তা শেষ হয়।
- কোনো নেভিগেশন এন্ট্রির ক্ষেত্রে, যখন এটিকে ব্যাক স্ট্যাক থেকে সরিয়ে ফেলা হয়।
- কম্পোজেবল-এর ক্ষেত্রে, যখন এটি কম্পোজিশন থেকে বেরিয়ে আসে। আপনি
rememberViewModelStoreOwnerব্যবহার করে একটি ViewModel-কে সরাসরি আপনার UI-এর যেকোনো অংশে (যেমন একটিPagerবাLazyList) স্কোপ করতে পারেন।
এই কারণে, কনফিগারেশন পরিবর্তনের পরেও ডেটা সংরক্ষণের জন্য ভিউমডেল একটি চমৎকার সমাধান।
চিত্র ১-এ একটি অ্যাক্টিভিটির ঘূর্ণন এবং সমাপ্তির বিভিন্ন জীবনচক্র অবস্থা দেখানো হয়েছে। চিত্রটিতে সংশ্লিষ্ট অ্যাক্টিভিটি জীবনচক্রের পাশে ViewModel জীবনকালও দেখানো হয়েছে। এই নির্দিষ্ট ডায়াগ্রামটি একটি অ্যাক্টিভিটির বিভিন্ন অবস্থা তুলে ধরে।

সাধারণত, সিস্টেম যখন প্রথমবার কোনো অ্যাক্টিভিটি অবজেক্টের onCreate() মেথড কল করে, তখনই আপনি একটি ViewModel জন্য অনুরোধ করেন। একটি অ্যাক্টিভিটির অস্তিত্বকালে সিস্টেম একাধিকবার onCreate() কল করতে পারে, যেমন যখন ডিভাইসের স্ক্রিন ঘোরানো হয়। আপনি যখন প্রথমবার একটি ViewModel জন্য অনুরোধ করেন, তখন থেকে অ্যাক্টিভিটিটি শেষ ও ধ্বংস না হওয়া পর্যন্ত ViewModel টি বিদ্যমান থাকে।
ViewModel নির্ভরতাগুলি পরিষ্কার করা
ViewModelStoreOwner যখন তার জীবনচক্র চলাকালীন ViewModel-কে ধ্বংস করে, তখন এটি onCleared মেথডটিকে কল করে। এর ফলে আপনি ViewModel-এর জীবনচক্রের পরবর্তী যেকোনো কাজ বা নির্ভরতা পরিষ্কার করতে পারেন।
নিম্নলিখিত উদাহরণটি viewModelScope এর একটি বিকল্প দেখায়। viewModelScope হলো একটি বিল্ট-ইন CoroutineScope যা স্বয়ংক্রিয়ভাবে ViewModel-এর লাইফসাইকেল অনুসরণ করে। ViewModel ব্যবসায়িক কার্যক্রম শুরু করার জন্য এটি ব্যবহার করে। যদি আপনি সহজ পরীক্ষার জন্য viewModelScope এর পরিবর্তে একটি কাস্টম স্কোপ ব্যবহার করতে চান, তাহলে ViewModel তার কনস্ট্রাক্টরে একটি ডিপেন্ডেন্সি হিসেবে CoroutineScope গ্রহণ করতে পারে। যখন ViewModelStoreOwner তার লাইফসাইকেলের শেষে ViewModel-কে ক্লিয়ার করে, তখন ViewModel CoroutineScope টিও বাতিল করে দেয়।
class MyViewModel(
private val coroutineScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {
// Other ViewModel logic ...
override fun onCleared() {
coroutineScope.cancel()
}
}
লাইফসাইকেল ভার্সন ২.৫ এবং তার উপরের ভার্সন থেকে, আপনি ViewModel-এর কনস্ট্রাক্টরে এক বা একাধিক Closeable অবজেক্ট পাস করতে পারেন, যা ViewModel ইনস্ট্যান্সটি খালি করা হলে স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যায়।
class CloseableCoroutineScope(
context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
class MyViewModel(
private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
// Other ViewModel logic ...
}
সর্বোত্তম অনুশীলন
ViewModel প্রয়োগ করার সময় আপনার অনুসরণ করা উচিত এমন কয়েকটি গুরুত্বপূর্ণ সেরা অনুশীলন নিচে দেওয়া হলো:
- এদের স্কোপিং- এর কারণে, ViewModel-গুলোকে স্ক্রিন লেভেল স্টেট হোল্ডারের ইমপ্লিমেন্টেশন ডিটেইলস হিসেবে ব্যবহার করুন। চিপ গ্রুপ বা ফর্মের মতো পুনঃব্যবহারযোগ্য UI কম্পোনেন্টের স্টেট হোল্ডার হিসেবে এগুলো ব্যবহার করবেন না। অন্যথায়, আপনি যদি প্রতিটি চিপের জন্য একটি সুস্পষ্ট ভিউ মডেল কী ব্যবহার না করেন, তাহলে একই UI কম্পোনেন্টের বিভিন্ন ব্যবহারে একই ViewModelStoreOwner-এর অধীনে একই ViewModel ইনস্ট্যান্স পাবেন।
- ViewModel-এর UI বাস্তবায়নের খুঁটিনাটি জানা উচিত নয়। ViewModel API যে মেথডগুলো প্রকাশ করে এবং UI স্টেট ফিল্ডগুলোর নাম যতটা সম্ভব জেনেরিক রাখুন। এইভাবে, আপনার ViewModel যেকোনো ধরনের UI-এর সাথে মানিয়ে নিতে পারবে: একটি মোবাইল ফোন, ফোল্ডেবল, ট্যাবলেট, বা এমনকি একটি ক্রোমবুকও!
- যেহেতু
ViewModelStoreOwnerএর চেয়ে ViewModel-গুলো সম্ভাব্যভাবে বেশিদিন টিকে থাকতে পারে, তাই মেমরি লিক প্রতিরোধ করার জন্য এগুলোর মধ্যেContextবাResourcesমতো লাইফসাইকেল-সম্পর্কিত API-এর কোনো রেফারেন্স থাকা উচিত নয়। - ViewModel-কে অন্য ক্লাস, ফাংশন বা UI কম্পোনেন্টে পাস করবেন না। যেহেতু প্ল্যাটফর্ম এগুলো পরিচালনা করে, তাই এগুলোকে প্ল্যাটফর্মের যতটা সম্ভব কাছাকাছি রাখা উচিত—আপনার Activity, স্ক্রিন লেভেলের কম্পোজেবল ফাংশন বা নেভিগেশন ডেস্টিনেশনের কাছাকাছি। এর ফলে নিম্ন স্তরের কম্পোনেন্টগুলো তাদের প্রয়োজনের চেয়ে বেশি ডেটা এবং লজিক অ্যাক্সেস করতে পারে না।
আরও তথ্য
আপনার ডেটা আরও জটিল হয়ে উঠলে, শুধুমাত্র ডেটা লোড করার জন্য আপনি একটি আলাদা ক্লাস রাখতে পারেন। ViewModel এর উদ্দেশ্য হলো একটি UI কন্ট্রোলারের ডেটাকে এনক্যাপসুলেট করা, যাতে কনফিগারেশন পরিবর্তনের পরেও ডেটা অক্ষত থাকে। কনফিগারেশন পরিবর্তনের পরেও কীভাবে ডেটা লোড, পারসিস্ট এবং ম্যানেজ করতে হয়, সে সম্পর্কে তথ্যের জন্য সেভড UI স্টেটস (Saved UI States) দেখুন।
অ্যান্ড্রয়েড অ্যাপ আর্কিটেকচার গাইড এই ফাংশনগুলো পরিচালনা করার জন্য একটি রিপোজিটরি ক্লাস তৈরি করার পরামর্শ দেয়।
অতিরিক্ত সম্পদ
ViewModel ক্লাস সম্পর্কে আরও তথ্যের জন্য নিম্নলিখিত উৎসগুলো দেখুন।
ডকুমেন্টেশন
বিষয়বস্তু দেখুন
নমুনা
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলেও লিঙ্কের লেখা প্রদর্শিত হয়।
- লাইফসাইকেল-অ্যাওয়্যার কম্পোনেন্টের সাথে কোটলিন কোরাউটিন ব্যবহার করুন
- UI অবস্থা সংরক্ষণ করুন
- পৃষ্ঠা ডেটা লোড এবং প্রদর্শন করুন
