CoroutineScope একটি Kotlin ইন্টারফেস যা করুটিনের জীবনচক্র নির্ধারণ করে এবং নতুন করুটিন চালানোর জন্য কনটেক্সট প্রদান করে। Kotlin ডকুমেন্টেশন, 2025 অনুসারে, প্রতিটি CoroutineScope ইনস্ট্যান্সে CoroutineContext থাকে এবং এর ভিতরে চালানো সমস্ত করুটিন পরিচালনা করে। যখন scope বাতিল (cancel) করা হয়, তখন সমস্ত চাইল্ড করুটিন স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়, যা মেমরি লিক প্রতিরোধ করে।
মূল পয়েন্ট
CoroutineScope kotlinx.coroutines লাইব্রেরির একটি মৌলিক ইন্টারফেস যা করুটিনের জন্য ধারক হিসেবে কাজ করে। এটি করুটিন জীবনচক্রের সীমানা নির্ধারণ করে: যখন scope সম্পূর্ণ হয়, তখন এর ভিতরের সমস্ত করুটিন স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়।
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}
ইন্টারফেসে শুধুমাত্র একটি ফিল্ড রয়েছে — coroutineContext। এর মাধ্যমে, scope এর ভিতরে চালানো সমস্ত করুটিনের জন্য একটি ডিসপ্যাচার (Dispatcher), জব (Job), এক্সেপশন হ্যান্ডলার এবং অন্যান্য কনটেক্সট উপাদান প্রদান করে।
করুটিন চালানোর সমস্ত ফাংশন — launch, async, runBlocking — CoroutineScope-এ এক্সটেনশন ফাংশন। এর মানে হল এগুলো শুধুমাত্র scope অবজেক্ট উপলব্ধ থাকলেই কল করা যেতে পারে। এই ডিজাইন নিশ্চিত করে যে প্রতিটি করুটিনের একটি স্পষ্টভাবে সংজ্ঞায়িত প্যারেন্ট এবং জীবনচক্র রয়েছে।
Android-এ, প্রতিটি আর্কিটেকচারাল কম্পোনেন্টের নিজস্ব scope থাকে: ViewModel-এর জন্য viewModelScope, Activity/Fragment-এর জন্য lifecycleScope। সার্ভার অ্যাপ্লিকেশনে, scope একটি HTTP রিকোয়েস্ট বা ডেটাবেস কানেকশন পুলের সাথে আবদ্ধ করা যেতে পারে।
CoroutineScope-এর অভ্যন্তরীণ কার্যপ্রণালী বোঝার জন্য Job-এর ধারণা এবং স্ট্রাকচার্ড কনকারেন্সি-র নীতির সাথে পরিচিতি প্রয়োজন।
প্রতিটি করুটিন চালু হওয়ার সময় একটি Job অবজেক্ট (বা async-এর জন্য Deferred) ফেরত দেয়। Job একটি সীমিত জীবনচক্র বিশিষ্ট কাজ উপস্থাপন করে: New, Active, Completing, Completed, Cancelling, Cancelled। Job অবজেক্টগুলো একটি ট্রি স্ট্রাকচার গঠন করে:
স্ট্রাকচার্ড কনকারেন্সি Kotlin Coroutines-এর একটি মূল আর্কিটেকচারাল নীতি, যেখানে করুটিনের জীবনচক্র তার scope-এর জীবনচক্রের সাথে আবদ্ধ। এটি "ফায়ার-এন্ড-ফরগেট" মডেলের বিপরীত, যেখানে scope সম্পূর্ণ হওয়ার পরেও করুটিন বেঁচে থাকে। স্ট্রাকচার্ড কনকারেন্সির সুবিধা:
যখন scope.cancel() কল করা হয়, scope-এর Job Cancelled অবস্থায় চলে যায়, যা পুনরাবৃত্তিমূলকভাবে সমস্ত চাইল্ড Job বাতিল করে। বাতিলকরণের পরে, শুধুমাত্র নতুন CoroutineScope ইনস্ট্যান্স তৈরি করলেই scope পুনরায় ব্যবহার করা যেতে পারে।
আপনি ফ্যাক্টরি ফাংশনের মাধ্যমে অথবা আপনার ক্লাসে ইন্টারফেস ইমপ্লিমেন্ট করে CoroutineScope তৈরি করতে পারেন। উভয় পদ্ধতি দেখি।
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
scope.launch {
println("Running on ${Thread.currentThread().name}")
}
ফ্যাক্টরি ফাংশন একটি CoroutineContext গ্রহণ করে এবং নির্দিষ্ট কনটেক্সট সহ scope তৈরি করে। উদাহরণটি CPU-নিবিড় কাজের জন্য Dispatchers.Default এবং SupervisorJob ব্যবহার করে, যা চাইল্ড করুটিনের মধ্যে এক্সেপশন আলাদা করে।
class MyRepository {
private val scope = CoroutineScope(Dispatchers.IO + Job())
suspend fun fetchData(): Data = scope.async {
api.getData()
}.await()
fun cleanup() {
scope.cancel()
}
}
আমরা scope কে ক্লাস ফিল্ড হিসেবে সংরক্ষণ করি এবং এটি বাতিল করতে ম্যানুয়ালি cleanup কল করি। এই পদ্ধতি পরিচালিত জীবনচক্রযুক্ত কম্পোনেন্ট — যেমন রিপোজিটরি বা ম্যানেজার — এর জন্য উপযুক্ত।
Kotlin by কীওয়ার্ডের মাধ্যমে CoroutineScope ইমপ্লিমেন্টেশন ডেলিগেট করার অনুমতি দেয়:
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
fun load() {
launch {
// coroutine runs in DataLoader scope
}
}
}
এই পদ্ধতি সুবিধাজনক যখন ক্লাস নিজেই একটি scope এবং করুটিন চালু করার পদ্ধতি প্রদান করতে চায়। তবে সতর্ক থাকুন: ক্লাস CoroutineScope-এর সমস্ত পদ্ধতি ইনহেরিট করে, যার মধ্যে cancel-ও অন্তর্ভুক্ত, যা এনক্যাপসুলেশন ভাঙতে পারে।
GlobalScope সম্পূর্ণ অ্যাপ্লিকেশনের জন্য একটি সিঙ্গেলটন CoroutineScope। প্রোডাকশন কোডে এর ব্যবহার আনুষ্ঠানিকভাবে সুপারিশ করা হয় না।
JetBrains GlobalScope শুধুমাত্র বিরল পরিস্থিতিতে অনুমতি দেয়: অ্যাপ্লিকেশন-স্তরের ব্যাকগ্রাউন্ড প্রক্রিয়া যা সমস্ত Activity বন্ধ হওয়ার পরেও চলা উচিত (যেমন ডেটা সিঙ্ক্রোনাইজেশন, অ্যানালিটিক্স)। তবে এই ক্ষেত্রেও, CoroutineScope(SupervisorJob()) দিয়ে নিজস্ব scope তৈরি করা পছন্দনীয়।
সর্বদা কাস্টম CoroutineScope ব্যবহার করুন স্পষ্ট জীবনচক্র ব্যবস্থাপনা সহ। Android-এ, এগুলো হল viewModelScope এবং lifecycleScope। সার্ভার অ্যাপ্লিকেশনে, প্রতিটি রিকোয়েস্ট বা কানেকশন পুলের জন্য scope তৈরি করুন।
উভয় ফাংশনই সাসপেন্ড ফাংশন যা সমান্তরাল কাজের জন্য অস্থায়ী scope তৈরি করে, কিন্তু এক্সেপশনের সাথে তাদের আচরণ মৌলিকভাবে ভিন্ন।
| বৈশিষ্ট্য | coroutineScope | supervisorScope |
|---|---|---|
| ত্রুটিতে আচরণ | চাইল্ড করুটিনে এক্সেপশন বাকি সব বাতিল করে | চাইল্ড করুটিনে এক্সেপশন অন্যগুলো বাতিল করে না |
| ত্রুটি প্রসার | হ্যাঁ, প্রথম এক্সেপশন বাইরে ছড়িয়ে পড়ে | হ্যাঁ, প্রথম এক্সেপশন বাইরে ছড়িয়ে পড়ে |
| ডিফল্ট Job | Job() — চাইল্ড প্যারেন্টের সাথে আবদ্ধ | SupervisorJob() — চাইল্ড একে অপরের উপর নির্ভরশীল নয় |
| সাধারণ ব্যবহার-ক্ষেত্র | একাধিক ধাপের পারমাণবিক অপারেশন | স্বাধীন সমান্তরাল কাজ (UI লোড) |
coroutineScope ব্যবহার করুন যখন একাধিক সমান্তরাল অপারেশন একটি একক পারমাণবিক অপারেশন গঠন করে। উদাহরণস্বরূপ, তিনটি সার্ভার থেকে ডেটা লোড করা: যদি একটি রিকোয়েস্ট ব্যর্থ হয়, বাকিগুলো অর্থহীন।
suspend fun loadProductPage(): ProductPage = coroutineScope {
val product = async { api.getProduct() }
val reviews = async { api.getReviews() }
ProductPage(product.await(), reviews.await())
}
যদি getProduct বা getReviews এক্সেপশন ফেলে — উভয় করুটিন বাতিল হয়ে যায় এবং এক্সেপশন কলার কোডে ছড়িয়ে পড়ে।
supervisorScope ব্যবহার করুন যখন সমান্তরাল অপারেশন একে অপরের উপর নির্ভরশীল নয়। উদাহরণস্বরূপ, একাধিক স্বাধীন বিভাগে প্রোফাইল ডেটা লোড করা: যদি রেকমেন্ডেশন বিভাগ ব্যর্থ হয়, প্রোফাইল হেডার এবং বন্ধু তালিকা প্রদর্শিত হওয়া উচিত।
Kotlin-এ CoroutineScope ব্যবহার করার সময় ডেভেলপারদের সবচেয়ে সাধারণ ভুলগুলো দেখি।
করুটিন লিকের সবচেয়ে সাধারণ পরিস্থিতি হল কম্পোনেন্ট শেষ হলে cancel কল না করে scope তৈরি করা। যদি scope বাতিল না করা হয়, করুটিন চলতে থাকে, অবজেক্টের রেফারেন্স ধরে রাখে। Android-এ, viewModelScope বা lifecycleScope ব্যবহার করুন, যা স্বয়ংক্রিয়ভাবে বাতিল হয়।
GlobalScope Android কম্পোনেন্ট জীবনচক্র উপেক্ষা করে। Activity বন্ধ হওয়ার পর GlobalScope-এ চালু করা করুটিন চলতে থাকবে এবং UI আপডেট করার চেষ্টা করবে — যা ক্র্যাশের কারণ হবে। UI কম্পোনেন্টের জন্য সর্বদা lifecycleScope ব্যবহার করুন।
cancel() কল করার পর, scope পুনরায় ব্যবহার করা যায় না — এর ভিতরের সমস্ত করুটিন ইতিমধ্যে সম্পূর্ণ। ফ্যাক্টরি ফাংশনের মাধ্যমে নতুন CoroutineScope ইনস্ট্যান্স তৈরি করুন। Job() পুনরায় সক্রিয়করণ সমর্থন করে না।
by দিয়ে ডেলিগেট করলে, ক্লাস একটি পাবলিক cancel() পদ্ধতি পায় যা যেকোনো স্থান থেকে কল করা যেতে পারে, এনক্যাপসুলেশন ভেঙে। ইন্টারফেস ডেলিগেট করার পরিবর্তে scope কে প্রাইভেট ফিল্ড হিসেবে সংরক্ষণ করুন।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
CoroutineScope একটি ইন্টারফেস যা CoroutineContext-এর মালিক এবং করুটিন জীবনচক্রের জন্য দায়ী। CoroutineContext হল উপাদানের (ডিসপ্যাচার, job, ত্রুটি হ্যান্ডলার) একটি সেট যা নির্ধারণ করে করুটিন "কীভাবে" সম্পাদিত হয়। একটি পার্থক্য: scope করুটিন তৈরি করে, যখন কনটেক্সট তাদের আচরণ নিয়ন্ত্রণ করে।
হ্যাঁ, এটি একটি স্ট্যান্ডার্ড প্যাটার্ন: CoroutineScope(Dispatchers.IO + SupervisorJob())। SupervisorJob একটি চাইল্ড করুটিনে এক্সেপশন হলে বাকিগুলোর ক্যাসকেডিং বাতিলকরণ প্রতিরোধ করে। এটি স্বাধীন সমান্তরাল কাজের জন্য উপযোগী যেখানে একটির ত্রুটি অন্যগুলো থামানো উচিত নয়।
scope-এ করুটিনের কোনো সীমা নেই — এগুলো শুধুমাত্র উপলব্ধ মেমরি এবং ডিসপ্যাচার সেটিংস দ্বারা সীমিত। ব্যবহারিক সীমা সাধারণত একটি scope-এ হাজার হাজার সক্রিয় করুটিন। তবে, বিপুল সংখ্যক করুটিন আর্কিটেকচারাল সমস্যা নির্দেশ করতে পারে।
সঠিক পদ্ধতি হল কনস্ট্রাক্টরের মাধ্যমে ক্লাসে scope পাস করা অথবা kotlinx-coroutines-test থেকে runBlockingTest / runTest ব্যবহার করা। পরীক্ষায়, আপনি scope কে TestCoroutineDispatcher দিয়ে প্রতিস্থাপন করতে পারেন এবং ম্যানুয়ালি করুটিন এক্সিকিউশন নিয়ন্ত্রণ করতে পারেন।
না, scope একটি করুটিনের জন্য বাহ্যিক ধারক। করুটিন নিজেই scope নয়। তবে, করুটিনের ভিতরে আপনি coroutineScope বা supervisorScope-এর মাধ্যমে চাইল্ড করুটিন সমান্তরালে চালানোর জন্য নতুন scope তৈরি করতে পারেন।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন