viewModelScope: এটি কী, ViewModel এর সাথে সংযোগ এবং Android এ কাজ

লেখক: IT Sectr প্রকাশিত: 2026-06-23 পড়ার সময়: 9 মিনিট

viewModelScope হলো androidx.lifecycle লাইব্রেরি থেকে একটি বিল্ট-ইন CoroutineScope যা ViewModel জীবনচক্রের সাথে সংযুক্ত এবং এটি পরিষ্কার হলে স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়। Google Android Developers, 2025 অনুসারে, viewModelScope হল MVVM আর্কিটেকচারে করুটিন চালু করার মানক পদ্ধতি, যা মেমরি লিকের ঝুঁকি ছাড়াই নিরাপদ অ্যাসিনক্রোনাস অপারেশন নিশ্চিত করে। ViewModelScope ডিফল্টরূপে Dispatchers.Main ব্যবহার করে, এবং এর ভিতরে সমস্ত IO অপারেশন withContext এর মাধ্যমে সম্পাদন করতে হবে।

মূল পয়েন্ট

  • viewModelScope — lifecycle-viewmodel-ktx থেকে CoroutineScope, ViewModel.onCleared() এ বাতিল হয়
  • Dispatchers.Main — ডিফল্ট ডিসপ্যাচার, তাই করুটিনের ভিতরে UI আপডেট নিরাপদ
  • onCleared — কলব্যাক যা viewModelScope-এ সমস্ত সক্রিয় করুটিন স্বয়ংক্রিয়ভাবে বাতিল করে
  • clear() বনাম onCleared() — clear() ফ্রেমওয়ার্ক দ্বারা onCleared-এর আগে কল করা হয়, যা scope বাতিল নিশ্চিত করে
  • launch — fire-and-forget অপারেশনের জন্য viewModelScope-এ করুটিন শুরু করার প্রধান উপায়

Android এ viewModelScope কী?

viewModelScope হলো ViewModel ইন্টারফেসের একটি এক্সটেনশন প্রপার্টি, যা lifecycle-viewmodel-ktx লাইব্রেরিতে (সংস্করণ 2.1.0 থেকে) যোগ করা হয়েছে। এটি ViewModel জীবনচক্রের সাথে সংযুক্ত একটি প্রস্তুত CoroutineScope সরবরাহ করে।

kotlin
// Internal structure (simplified)
val ViewModel.viewModelScope: CoroutineScope
    get() {
        val scope = this.getTag(JOB_KEY)
        if (scope != null) return scope
        return CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
            .also { setTag(JOB_KEY, it) }
    }

প্রথম অ্যাক্সেসে scope লেজি (lazy) ভাবে তৈরি হয় এবং setTag এর মাধ্যমে ক্যাশে করা হয়। এটি SupervisorJob ব্যবহার করে, যার অর্থ একটি চাইল্ড করুটিনে ব্যতিক্রম অন্যগুলোকে বাতিল করে না। ডিফল্ট ডিসপ্যাচার হল Dispatchers.Main.immediate, যা ইতিমধ্যে Main থ্রেডে থাকলে অতিরিক্ত ডিসপ্যাচিং ছাড়াই মূল থ্রেডে কোড executes করে।

কীভাবে viewModelScope পরিষ্কারের বিজ্ঞপ্তি পায়

যখন ViewModel জীবনচক্র ত্যাগ করে (Activity শেষ হয় বা Fragment সরানো হয়), সিস্টেম clear() কল করে, যা onCleared() ট্রিগার করে। এই কলব্যাকে, viewModelScope তার Job বাতিল করে, যা পুনরাবৃত্তভাবে সমস্ত সক্রিয় করুটিন শেষ করে। প্রক্রিয়াটি Closeable ইন্টারফেসের মাধ্যমে বাস্তবায়িত হয়, যেখানে scope Job স্বয়ংক্রিয় বন্ধের জন্য একটি রিসোর্স হিসাবে নিবন্ধিত হয়।

viewModelScope কীভাবে কাজ করে: ViewModel জীবনচক্রের সাথে সংযোগ

viewModelScope-এর ViewModel জীবনচক্রের সাথে সংযোগের প্রক্রিয়াটি ট্যাগিং এবং onCleared কলব্যাকের উপর ভিত্তি করে তৈরি। চলুন ধাপে ধাপে দেখি।

ধাপ 1: প্রথম অ্যাক্সেসে scope তৈরি

যখন ViewModel viewModelScope.launch { ... } executes করে, getter চেক করে JOB_KEY ট্যাগের অধীনে আগে থেকে কোনো scope সংরক্ষিত আছে কিনা। যদি scope না থাকে, তাহলে একটি নতুন CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) instance তৈরি হয়। Scope একটি অভ্যন্তরীণ ট্যাগ ম্যাপের মাধ্যমে ViewModel-এর ভিতরে সংরক্ষিত হয়।

ধাপ 2: করুটিন জীবনচক্র

viewModelScope.launch বা viewModelScope.async এর মাধ্যমে চালু করা সমস্ত করুটিন scope-এর SupervisorJob-এর চাইল্ড হয়ে যায়। তারা মূল থ্রেডে চলে (যদি not withContext এর মাধ্যমে ভিন্ন ডিসপ্যাচার নির্দিষ্ট করা না থাকে)। যতক্ষণ ViewModel জীবিত, করুটিন সক্রিয়, স্থগিত বা সম্পূর্ণ থাকতে পারে।

ধাপ 3: onCleared-এ বাতিলকরণ

যখন সিস্টেম ViewModel ধ্বংস করে, ViewModel.clear() কল করা হয়। clear()-এর ভিতরে নিম্নলিখিত ঘটনাগুলো ঘটে:

  • কাস্টম পরিষ্কার লজিকের জন্য onCleared() কল করা হয়
  • addCloseable এর মাধ্যমে নিবন্ধিত সমস্ত Closeable রিসোর্স বন্ধ করা হয়
  • viewModelScope-এর Job Cancelled অবস্থায় চলে যায়
  • সমস্ত চাইল্ড করুটিন পুনরাবৃত্তভাবে বাতিল করা হয়
  • আবর্জনা সংগ্রহকের জন্য scope-এর রেফারেন্স মুক্ত করা হয়

রোটেশন সহনশীলতা

স্ক্রিন ঘোরালে, Activity পুনরায় তৈরি হয়, কিন্তু ViewModel টিকে থাকে (ViewModelStoreOwner-এর ধন্যবাদ)। এর অর্থ viewModelScope সক্রিয় থাকে এবং করুটিনগুলি বাধা ছাড়াই চলতে থাকে। Activity পুনরায় তৈরি হওয়ার পরে, একই ViewModel (এবং একই scope) পুনরায় ব্যবহার করা হয় — ডেটা লোডিং শুরু থেকে শুরু হয় না।

MVVM আর্কিটেকচারে viewModelScope

MVVM (Model-View-ViewModel) Android অ্যাপ্লিকেশনের জন্য Google-এর প্রস্তাবিত আর্কিটেকচার। viewModelScope এতে অ্যাসিনক্রোনাস অপারেশনের নির্বাহক হিসাবে কেন্দ্রীয় ভূমিকা পালন করে।

আর্কিটেকচার স্তরে viewModelScope-এর ভূমিকা

স্তরউপাদানviewModelScope-এর ভূমিকা
UIActivity / FragmentViewModel থেকে StateFlow/LiveData পর্যবেক্ষণ করে
ViewModelViewModelviewModelScope এর মাধ্যমে করুটিন চালু করে, UI অবস্থা পরিচালনা করে
RepositoryRepositoryviewModelScope করুটিন থেকে কল করার জন্য suspend ফাংশন সরবরাহ করে
DataDAO / Apiপ্রকৃত অনুরোধ সম্পাদন করে (Room, Retrofit)

ViewModel viewModelScope এর মাধ্যমে করুটিন চালু করে, যার ভিতরে এটি Repository-এর suspend ফাংশন কল করে। ফলাফল StateFlow-এ রূপান্তরিত হয়, যা UI স্তর পর্যবেক্ষণ করে। এই ডিজাইন দায়িত্বের স্পষ্ট বিভাজন এবং প্রতিটি স্তরের স্বাধীন পরীক্ষাযোগ্যতা নিশ্চিত করে।

কেন ViewModel-এ viewModelScope, Fragment-এ নয়

যদি করুটিন Fragment থেকে চালু করা হতো, স্ক্রিন ঘোরালে Fragment ধ্বংসের সাথে সেগুলো বাতিল হয়ে যেত। ViewModel রোটেশনে টিকে থাকে, তাই এর scope-এ চালু করা করুটিন চলতে থাকে। ডেটা লোড করার সময় lifecycleScope-এর তুলনায় viewModelScope-এর এটাই মূল সুবিধা।

viewModelScope ব্যবহারের উদাহরণ

আসুন Kotlin এ Android অ্যাপ্লিকেশনে viewModelScope ব্যবহারের তিনটি ব্যবহারিক দৃশ্য দেখি।

উদাহরণ 1: ViewModel তৈরি করার সময় ডেটা লোড করা

kotlin
class ProfileViewModel(
    private val repo: ProfileRepository
) : ViewModel() {

    private val _profile = MutableStateFlow<Profile?>(null)
    val profile: StateFlow<Profile?> = _profile

    init {
        loadProfile()
    }

    private fun loadProfile() {
        viewModelScope.launch {
            val result = repo.getProfile()
            _profile.value = result
        }
    }
}

init ব্লকে, প্রোফাইল লোডিং অবিলম্বে শুরু হয়। করুটিন মূল থ্রেডে (ডিফল্টরূপে) চলে। রিপোজিটরি তার suspend ফাংশনের ভিতরে নেটওয়ার্ক অনুরোধের জন্য withContext(Dispatchers.IO) ব্যবহার করে, তাই ViewModel-কে থ্রেড সুইচিং নিয়ে ভাবতে হয় না।

উদাহরণ 2: sealed class এর মাধ্যমে ত্রুটি পরিচালনা

kotlin
sealed class UiState {
    object Loading : UiState()
    data class Success(val data: List<Item>) : UiState()
    data class Error(val message: String) : UiState()
}
kotlin
fun fetchItems() {
    _state.value = UiState.Loading
    viewModelScope.launch {
        try {
            val items = repo.getItems()
            _state.value = UiState.Success(items)
        } catch (e: Exception) {
            _state.value = UiState.Error(e.message ?: "Unknown error")
        }
    }
}

UI অবস্থা একটি sealed class UiState এর মাধ্যমে বর্ণিত হয়। ViewModel প্রতিটি পরিবর্তনে অবস্থা আপডেট করে। Fragment StateFlow-এ সাবস্ক্রাইব করে এবং শুধুমাত্র বর্তমান অবস্থায় প্রতিক্রিয়া জানায়, আগের রোটেশন থেকে পুরানো কল উপেক্ষা করে।

উদাহরণ 3: নতুন অনুরোধে পূর্ববর্তী করুটিন বাতিল করা

kotlin
private var searchJob: Job? = null

fun search(query: String) {
    searchJob?.cancel()
    searchJob = viewModelScope.launch {
        delay(300)
        val results = repo.search(query)
        _searchResults.value = results
    }
}

প্রতিটি নতুন অনুসন্ধান কোয়েরিতে, পূর্ববর্তী করুটিন বাতিল হয়। delay(300) ডিবাউন্স প্রয়োগ করে — অনুসন্ধান শুধুমাত্র 300 মিলিসেকেন্ড নিষ্ক্রিয়তার পরে সম্পাদন হয়। এটি সার্ভার লোড কমায় এবং পুরানো ফলাফল প্রতিরোধ করে।

viewModelScope বনাম lifecycleScope: কখন কী বেছে নেবেন

উভয় scope AndroidX Lifecycle লাইব্রেরি দ্বারা সরবরাহিত, কিন্তু ভিন্ন ভিন্ন জীবনচক্রের সাথে সংযুক্ত। পছন্দ কাজের ধরণের উপর নির্ভর করে।

Scope তুলনা

বৈশিষ্ট্যviewModelScopelifecycleScope
মালিকViewModelLifecycleOwner (Activity/Fragment)
রোটেশনে বাতিলনা (ViewModel টিকে থাকে)হ্যাঁ (Activity পুনরায় তৈরি হয়)
ডিফল্ট ডিসপ্যাচারDispatchers.Main.immediateDispatchers.Main.immediate
উপলব্ধViewModelActivity, Fragment, Service
সাধারণ ব্যবহারডেটা লোডিং, ব্যবসায়িক যুক্তিUI মিথস্ক্রিয়া, অ্যানিমেশন

Google সুপারিশ

Google সমস্ত ডেটা লোডিং এবং প্রক্রিয়াকরণ কাজের জন্য viewModelScope ব্যবহার করার সুপারিশ করে। lifecycleScope UI জীবনচক্রের নির্দিষ্ট মুহূর্তের সাথে যুক্ত অপারেশনের জন্য ব্যবহার করা উচিত — উদাহরণস্বরূপ, প্রথম স্ক্রিন উপস্থিতিতে অ্যানিমেশন শুরু করা বা লোকেশন আপডেটে সাবস্ক্রাইব করা যা স্ক্রিন ছেড়ে গেলে বন্ধ হওয়া উচিত।

viewModelScope নিয়ে কাজ করার সময় সাধারণ ভুল

একটি সুপ্রতিষ্ঠিত Android API-তেও, ডেভেলপাররা সাধারণ ভুল করে থাকেন। চলুন চারটি সবচেয়ে সাধারণ সমস্যা দেখি।

ভুল 1: scope বাতিলের পরে UI আপডেট করা

সবচেয়ে মারাত্মক ভুল হল ViewModel পরিষ্কার হওয়ার পরে StateFlow বা LiveData আপডেট করার চেষ্টা করা। যদিও viewModelScope onCleared()-এ বাতিল হয়, একটি করুটিন প্রকৃত বাতিল কার্যকর হওয়ার আগে কোড executes করতে পারে। চেক করতে isActive ব্যবহার করুন বা catch-ব্লক সম্পূর্ণ হওয়ার উপর নির্ভর করুন।

ভুল 2: SupervisorJob বিবেচনা না করে করুটিন চালু করা

viewModelScope অভ্যন্তরীণভাবে SupervisorJob ব্যবহার করে, যা করুটিনের মধ্যে ত্রুটিগুলো আলাদা করে। তবে, যদি আপনি viewModelScope.launch-এর ভিতরে নিজস্ব Job() সহ করুটিন চালু করেন, সেই করুটিন SupervisorJob-এর চাইল্ড হয় কিন্তু অন্যান্য করুটিনে ত্রুটির কারণে বাতিল থেকে সুরক্ষিত থাকবে না।

ভুল 3: একটি scope-এ অনেক বেশি করুটিন

যদিও viewModelScope-এর কোনো কঠোর সীমা নেই, হাজার হাজার সক্রিয় করুটিন সিস্টেমকে ধীর করে দিতে পারে। দীর্ঘ ডেটা তালিকার জন্য, প্রতিটি আইটেমের জন্য পৃথক করুটিন তৈরি করার পরিবর্তে Flow কে collectLatest এর সাথে ব্যবহার করুন।

ভুল 4: viewModelScope এর পরিবর্তে GlobalScope ব্যবহার করা

যদি viewModelScope-এর পরিবর্তে ভুলবশত GlobalScope ইম্পোর্ট করা হয়, তাহলে ViewModel পরিষ্কার হলে করুটিন বাতিল হবে না। এটি মেমরি লিক এবং সম্ভাব্য ক্র্যাশের দিকে নিয়ে যায়। সর্বদা নিশ্চিত করুন যে করুটিনগুলি viewModelScope এর মাধ্যমে চালু করা হয়েছে, বিশেষ করে Fragment সাবক্লাসে।

প্রায়শই জিজ্ঞাসিত প্রশ্ন

আমি কি viewModelScope-এর ডিফল্ট ডিসপ্যাচার পরিবর্তন করতে পারি?

আপনি সরাসরি viewModelScope-এর ডিসপ্যাচার পরিবর্তন করতে পারবেন না — এটি Dispatchers.Main.immediate হিসেবে হার্ডকোড করা। তবে, করুটিনের ভিতরে আপনি withContext এর মাধ্যমে অন্য ডিসপ্যাচারে সুইচ করতে পারেন। পরীক্ষায় ডিসপ্যাচার পরিবর্তন করতে, Rule এর মাধ্যমে TestDispatcher ব্যবহার করুন।

কিভাবে আমি viewModelScope কে Repository-তে পাস করব?

Repository-তে scope পাস করবেন না — এটি আর্কিটেকচার নীতি লঙ্ঘন করে। Repository-র suspend ফাংশন সরবরাহ করা উচিত, এবং ViewModel নিজেই viewModelScope এর মাধ্যমে করুটিন পরিচালনা করে। যদি Repository-র scope প্রয়োজন হয়, তাহলে Clean Architecture-এর পক্ষে আর্কিটেকচার পুনর্বিবেচনা করুন।

কেন viewModelScope SupervisorJob ব্যবহার করে?

SupervisorJob নিশ্চিত করে যে একটি করুটিনে ব্যতিক্রম (যেমন, একাধিক স্বাধীন অনুরোধের মধ্যে একটি লোডিং ত্রুটি) অন্য করুটিনগুলিকে বাতিল না করে। এটি ViewModel পরিস্থিতির সাথে মেলে যেখানে বিভিন্ন স্ক্রিন স্বাধীন ডেটা লোড করে।

Jetpack Compose-এ কি viewModelScope উপলব্ধ?

হ্যাঁ, viewModelScope UI প্রকার (View System বা Jetpack Compose) নির্বিশেষে যেকোনো ViewModel-এ উপলব্ধ। Compose-এ, করুটিনও viewModelScope এর মাধ্যমে চালু করা হয়, যখন UI প্রভাবের জন্য LaunchedEffect এবং rememberCoroutineScope ব্যবহার করা হয়।

viewModelScope.cancel() কল করলে করুটিনের কী হয়?

viewModelScope.cancel() কল করা scope তৎক্ষণাৎ বাতিল করে — সমস্ত সক্রিয় করুটিন CancellationException-এর সাথে শেষ হয়। এর পরে viewModelScope.launch কল করা হলে, getter-এ পরবর্তী অ্যাক্সেসে স্বয়ংক্রিয়ভাবে একটি নতুন scope তৈরি হয়।

সারসংক্ষেপ

  • viewModelScope — ViewModel জীবনচক্রের সাথে সংযুক্ত CoroutineScope, onCleared()-এ স্বয়ংক্রিয়ভাবে বাতিল হয়
  • SupervisorJob + Dispatchers.Main — অভ্যন্তরীণ কনফিগারেশন যা ত্রুটি বিচ্ছিন্নতা এবং নিরাপদ UI অ্যাক্সেস নিশ্চিত করে
  • স্ক্রিন রোটেশন — ViewModel টিকে থাকে, তাই viewModelScope-এ করুটিন পুনরায় শুরু না করে চলতে থাকে
  • MVVM আর্কিটেকচার — viewModelScope ViewModel স্তরে অ্যাসিনক্রোনাস অপারেশনের জন্য কেন্দ্রীয় উপাদান
  • lifecycleScope — Activity/Fragment জীবনচক্রের সাথে যুক্ত অপারেশনের জন্য বিকল্প, ViewModel নয়
  • StateFlow — sealed class এর মাধ্যমে viewModelScope করুটিন থেকে UI-তে ডেটা পাঠানোর পছন্দের উপায়
  • GlobalScope বিপজ্জনক — viewModelScope-কে GlobalScope দিয়ে প্রতিস্থাপন করলে মেমরি লিক এবং অ্যাপ ক্র্যাশ হয়

আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব

IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।

প্রকল্প নিয়ে আলোচনা করুন

আরও পড়ুন