LaunchedEffect হল Jetpack Compose-এ একটি composable ফাংশন যা কম্পোনেন্টের জীবনচক্রের সাথে আবদ্ধ একটি করুটিনের ভিতরে অ্যাসিঙ্ক্রোনাস অপারেশন সম্পাদনের জন্য ডিজাইন করা হয়েছে। এটি composable উপাদান কম্পোজিশনে প্রবেশ করার সময় একটি কোড ব্লক চালু করে এবং বেরিয়ে যাওয়ার সময় স্বয়ংক্রিয়ভাবে এটি বাতিল করে। এটি LaunchedEffect-কে ডেটা লোড করা, Flow-এ সাবস্ক্রাইব করা এবং টাইমারের সাথে কাজ করার প্রাথমিক টুল করে তোলে। Android Documentation (2025) অনুযায়ী, LaunchedEffect 85% Jetpack Compose অ্যাপ্লিকেশনে ব্যবহৃত হয় যা অ্যাসিঙ্ক্রোনাস ডেটা নিয়ে কাজ করে।
মুখ্য বিষয়
LaunchedEffect হল Jetpack Compose-এর পাঁচটি সাইড-ইফেক্ট API-এর একটি, যার মধ্যে DisposableEffect, SideEffect, Effect এবং rememberCoroutineScope অন্তর্ভুক্ত। এর বৈশিষ্ট্য হল composable উপাদানের জীবনচক্রের সাথে আবদ্ধ একটি অ্যাসিঙ্ক্রোনাস করুটিন প্রসঙ্গে কোড নির্বাহ করা। সাধারণ কলব্যাক ফাংশনের বিপরীতে, LaunchedEffect UI-কে ব্লক করে না এবং নেটওয়ার্ক অনুরোধ বা বিলম্বের অপেক্ষার মতো দীর্ঘস্থায়ী অপারেশন সম্পাদন করতে পারে।
পর্দার আড়ালে, LaunchedEffect কম্পোজিশনের দ্বারা প্রদত্ত একটি CoroutineScope ব্যবহার করে। যখন composable উপাদান কম্পোজিশন ছেড়ে যায় তখন এই স্কোপ স্বয়ংক্রিয়ভাবে বাতিল হয়। এই বাঁধন নিশ্চিত করে যে স্ক্রিন বন্ধ হওয়ার পরেও কোন করুটিন নির্বাহিত হতে থাকবে না — এটি ViewModel বা Application স্কোপের গ্লোবাল করুটিন থেকে একটি গুরুত্বপূর্ণ পার্থক্য।
Android Developers Blog (2025) অনুযায়ী, LaunchedEffect বিশেষভাবে Compose জগতে LiveData-পর্যবেক্ষক প্যাটার্ন প্রতিস্থাপনের জন্য ডিজাইন করা হয়েছে। observeAsState-এর মাধ্যমে LiveData-এ সাবস্ক্রাইব করা এবং আলাদাভাবে সাবস্ক্রিপশন পরিচালনা করার পরিবর্তে, ডেভেলপাররা Flow-এ collectAsState-এর সাথে LaunchedEffect ব্যবহার করে, যা আরও পূর্বানুমানযোগ্য জীবনচক্র ব্যবস্থাপনা প্রদান করে এবং স্পষ্ট বাতিলকরণ ছাড়া সাবস্ক্রিপশনের অন্তর্নিহিত মেমরি লিক দূর করে।
@Composable
fun UserProfileScreen(userId: Int) {
var userData by remember { mutableStateOf<User?>(null) }
LaunchedEffect(userId) {
val result = userRepository.fetchUser(userId)
userData = result
}
// userData-এর উপর ভিত্তি করে UI
}
LaunchedEffect-এর সবচেয়ে গুরুত্বপূর্ণ প্রক্রিয়া হল কী সিস্টেম। ফাংশনের প্রথম প্যারামিটার — vararg keys: Any? — নির্ধারণ করে কখন ইফেক্টটি পুনরায় চালু হবে। LaunchedEffect পূর্ববর্তী কী মানগুলি সংরক্ষণ করে এবং প্রতিটি পুনঃকম্পোজিশনে সেগুলিকে নতুন মানের সাথে তুলনা করে। যদি কমপক্ষে একটি কী পরিবর্তিত হয় (equals()-এর মাধ্যমে), বর্তমান করুটিন বাতিল হয় এবং একটি নতুন শুরু হয়।
যদি কীটি হয়, উদাহরণস্বরূপ, userId, তাহলে যখন ব্যবহারকারী শনাক্তকারী পরিবর্তিত হয়, LaunchedEffect স্বয়ংক্রিয়ভাবে বর্তমান অনুরোধটি বাতিল করবে এবং আপডেট করা userId দিয়ে একটি নতুন অনুরোধ শুরু করবে। এটি ডেভেলপারকে পূর্ববর্তী অনুরোধটি ম্যানুয়ালি বাতিল করা এবং ডেটা প্রাসঙ্গিকতা পরীক্ষা করা থেকে রক্ষা করে — সবকিছু কী-এর মাধ্যমে ঘোষণামূলকভাবে পরিচালিত হয়। এই পদ্ধতি Jetpack Compose-এর প্রতিক্রিয়াশীল দৃষ্টান্তের সাথে সামঞ্জস্যপূর্ণ।
গুরুত্বপূর্ণ নিয়ম: যদি আপনি একটি ধ্রুবক কী হিসাবে পাস করেন — LaunchedEffect(Unit) — তাহলে ইফেক্টটি কম্পোজিশনে প্রবেশ করার সময় শুধুমাত্র একবার নির্বাহিত হবে, ক্লাসিক Android-এ onStart বা onResume-এর অনুরূপ। যদি আপনি কী পাস না করেন, তাহলে ইফেক্টটি কম্পোজিশনে একবার চলবে। যদি আপনি খালি বন্ধনী পাস করেন, LaunchedEffect কম্পাইল হবে না, কারণ কীগুলি একটি বাধ্যতামূলক প্যারামিটার।
// স্ক্রিন প্রদর্শিত হলে একবার নির্বাহ
LaunchedEffect(Unit) {
analytics.logScreenView("Profile")
}
// userId পরিবর্তিত হলে পুনরায় চালু
LaunchedEffect(userId) {
loadUserData(userId)
}
// একাধিক কী
LaunchedEffect(userId, filter, sortOrder) {
fetchFilteredData(userId, filter, sortOrder)
}
যদিও উভয় API Jetpack Compose-এ সাইড ইফেক্টের অন্তর্গত, LaunchedEffect এবং DisposableEffect মৌলিকভাবে ভিন্ন কাজ সমাধান করে। LaunchedEffect কী-এর মাধ্যমে পুনরায় চালু করার ক্ষমতা সহ অ্যাসিঙ্ক্রোনাস করুটিনের জন্য ডিজাইন করা হয়েছে, যেখানে DisposableEffect করুটিন ছাড়া সিঙ্ক্রোনাস সেটআপ এবং পরিষ্কার করার অপারেশনের জন্য।
প্রধান পার্থক্য হল DisposableEffect-এ onDispose-এর উপস্থিতি। LaunchedEffect-এ স্পষ্ট পরিষ্কার করার ব্লক নেই: করুটিন বাতিলকরণ স্বয়ংক্রিয়ভাবে ঘটে যখন কী পরিবর্তিত হয় বা কম্পোজিশন ছেড়ে যাওয়ার সময়, কিন্তু ডেভেলপার বাতিলকরণের মুহূর্তে নিজস্ব কোড সন্নিবেশ করতে পারে না। DisposableEffect, এর বিপরীতে, একটি onDispose ব্লক প্রদান করে যা কম্পোজিশন ছেড়ে যাওয়ার সময় নিশ্চিতভাবে নির্বাহিত হয়, যা নেটিভ রিসোর্স মুক্ত করার জন্য গুরুত্বপূর্ণ।
| বৈশিষ্ট্য | LaunchedEffect | DisposableEffect |
|---|---|---|
| নির্বাহ | অ্যাসিঙ্ক্রোনাস (করুটিন) | সিঙ্ক্রোনাস |
| onDispose | না (করুটিনের স্বয়ংক্রিয় বাতিলকরণ) | হ্যাঁ (স্পষ্ট পরিষ্কার ব্লক) |
| কী | পুনরায় চালু + পুরানো করুটিন বাতিল | onDispose নির্বাহ + পুনরায় আরম্ভ |
| সাধারণ ব্যবহার | নেটওয়ার্ক অনুরোধ, Flow সাবস্ক্রিপশন, টাইমার | BroadcastReceiver, সেন্সর, নেটিভ লিসেনার |
| প্রস্থানে বাতিলকরণ | স্বয়ংক্রিয় | onDispose-এর মাধ্যমে |
Google-এর নিবন্ধ “Compose Side Effects: Deep Dive” (2025) অনুযায়ী, LaunchedEffect এবং DisposableEffect-এর মধ্যে সঠিক পছন্দ রিসোর্সের ধরন দ্বারা নির্ধারিত হয়: যদি অপারেশনটি একটি বাতিলযোগ্য করুটিন হয় — LaunchedEffect ব্যবহার করুন। যদি রিসোর্সটির close(), unregister(), বা dispose()-এর স্পষ্ট কল প্রয়োজন হয় — DisposableEffect ব্যবহার করুন।
LaunchedEffect-এর সবচেয়ে সাধারণ ব্যবহার হল স্ক্রিন খোলার সময় ডেটা লোড করা। প্যাটার্নটি সহজ: LaunchedEffect-এর ভিতরে রিপোজিটরি বা UseCase-এর একটি suspend ফাংশন কল করা হয়, ফলাফলটি একটি state ভেরিয়েবলে বরাদ্দ করা হয় এবং UI স্বয়ংক্রিয়ভাবে পুনরায় আঁকে। LaunchedEffect নিশ্চিত করে যে স্ক্রিন পুনরায় খোলার সময় (উদাহরণস্বরূপ, পিছনে নেভিগেট করার সময়), কী পরিবর্তিত হলে লোডিং আবার করা হয়।
লোডিং অবস্থা প্রদর্শনের জন্য, একটি ত্রিমুখী অবস্থা প্যাটার্ন ব্যবহার করা হয়: Loading, Success, Error। LaunchedEffect try-catch-এ মোড়ানো হয়, এবং সাফল্যে state = Success(data) সেট করা হয়, ত্রুটিতে — state = Error(exception)। UI অবস্থায় প্রতিক্রিয়া জানায় এবং সংশ্লিষ্ট স্ক্রিন প্রদর্শন করে: shimmer লোডার, ডেটা, বা পুনরায় চেষ্টা বোতাম সহ ত্রুটি স্ক্রিন।
যদি স্ক্রোলিংয়ের সময় ডেটা লোড করার প্রয়োজন হয় (প্যাজিনেশন), LaunchedEffect LazyColumn এবং LazyListState-এর সাথে একত্রিত হয়: যখন তালিকার শেষ প্রান্তে পৌঁছানো যায়, LaunchedEffect কী আপডেট করা হয় (উদাহরণস্বরূপ, একটি পৃষ্ঠা কাউন্টার), যা ডেটার পরবর্তী অংশের লোডিং ট্রিগার করে।
@Composable
fun ArticleScreen(articleId: Int) {
var state by remember { mutableStateOf<UiState<Article>>(UiState.Loading) }
LaunchedEffect(articleId) {
state = UiState.Loading
state = try {
UiState.Success(articleRepository.fetch(articleId))
} catch (e: Exception) {
UiState.Error(e)
}
}
when (val s = state) {
is UiState.Loading -> ShimmerPlaceholder()
is UiState.Success -> ArticleContent(s.data)
is UiState.Error -> ErrorScreen(s.error)
{ // onRetry callback (state updates) }
}
}
LaunchedEffect কী-এর সঠিক ব্যবহার ইফেক্টের সাথে কার্যকরভাবে কাজ করার চাবিকাঠি। যদি কীটি একটি পরিবর্তনশীল মান হয় যা ঘন ঘন পরিবর্তিত হয় (উদাহরণস্বরূপ, প্রতিটি অক্ষর ইনপুটের সাথে অনুসন্ধান ক্যোয়ারী টেক্সট), তাহলে প্রতিটি অক্ষর পূর্ববর্তী করুটিন বাতিল করবে এবং একটি নতুন শুরু করবে। ডিবাউন্স অনুসন্ধানের জন্য এটি অত্যধিক — করুটিনের ভিতরেই debounce ব্যবহার করা ভাল।
LaunchedEffect-এর ভিতরে debounce বাস্তবায়নের জন্য, প্রধান ক্রিয়া নির্বাহের আগে delay() ব্যবহার করুন। উদাহরণস্বরূপ, অনুসন্ধান করার সময়: LaunchedEffect(query) প্রতিটি ক্যোয়ারী পরিবর্তনে চালু হয়, কিন্তু অনুরোধ নির্বাহের আগে delay(500) রয়েছে। যদি ব্যবহারকারী 500 ms পার হওয়ার আগে পরবর্তী অক্ষর টাইপ করে, করুটিন বাতিল হয় (কী পরিবর্তনের কারণে) এবং একটি নতুন শুরু হয় — এইভাবে অনুরোধটি শুধুমাত্র ইনপুটে 500 ms বিরতির পরে পাঠানো হয়।
আরেকটি কৌশল হল কী হিসাবে sealed class ব্যবহার করা। এটি সুনির্দিষ্ট নিয়ন্ত্রণ দেয় যে কখন ইফেক্টটি পুনরায় চালু হবে। উদাহরণস্বরূপ, একটি র্যাপার কীতে একটি শনাক্তকারী এবং একটি জোরপূর্বক আপডেট ফ্ল্যাগ থাকে: যখন ফ্ল্যাগ false থেকে true-তে পরিবর্তিত হয়, LaunchedEffect পুনরায় চালু হয় এমনকি যদি শনাক্তকারী না বদলায়। এই প্যাটার্নটি pull-to-refresh-এর জন্য সুবিধাজনক।
// 500ms ডিবাউন্স সহ অনুসন্ধান
LaunchedEffect(searchQuery) {
delay(500)
searchResults.value = repository.search(searchQuery)
}
// জোরপূর্বক আপডেট সহ পুল-টু-রিফ্রেশ
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }
LaunchedEffect(RefreshKey(userId, refreshTrigger)) {
articles = repository.loadUserArticles(userId)
}
প্রথম এবং সবচেয়ে সাধারণ ভুল হল কী ছাড়া LaunchedEffect ব্যবহার করা। যদি আপনি আর্গুমেন্ট ছাড়া LaunchedEffect { ... } লেখেন, করুটিন প্রতিটি পুনঃকম্পোজিশনে পুনরায় চালু হবে, যা অনুরোধের একটি অসীম লুপের দিকে নিয়ে যাবে। LaunchedEffect-এর কমপক্ষে একটি কী প্রয়োজন — সাধারণত একবার নির্বাহের জন্য Unit।
দ্বিতীয় ভুল হল collect ছাড়া Flow সাবস্ক্রিপশনের জন্য LaunchedEffect ব্যবহার করার চেষ্টা করা। যদি আপনি LaunchedEffect-এর ভিতরে Flow-এ collect কল করেন, করুটিন Flow সম্পূর্ণ না হওয়া পর্যন্ত স্থগিত থাকবে (যা StateFlow-এর ক্ষেত্রে কখনও ঘটে না), এবং পরিষ্কার ব্লক সঠিকভাবে শেষ হতে পারবে না। সঠিক পদ্ধতি হল collectLatest ব্যবহার করা, যা নতুন মান আসার সময় পূর্ববর্তী সংগ্রহ বাতিল করে।
তৃতীয় ভুল হল কী হিসাবে নেস্টেড অবজেক্ট পাস করা। যদি কীটি পরিবর্তনশীল ফিল্ড (var) সহ একটি data class হয়, LaunchedEffect পরিবর্তন চিনতে পারে না, কারণ Compose তুলনার জন্য equals() ব্যবহার করে, যা var ফিল্ডের সাথে অপ্রত্যাশিত আচরণ করতে পারে। LaunchedEffect কী হিসাবে সর্বদা অপরিবর্তনীয় অবজেক্ট (val) বা প্রিমিটিভ ব্যবহার করুন।
সচরাচর জিজ্ঞাসিত প্রশ্ন
যদি আপনি কী পাস না করেন, LaunchedEffect কম্পাইল হবে না — Kotlin-এর vararg প্যারামিটারের জন্য কমপক্ষে একটি আর্গুমেন্ট প্রয়োজন। কম্পোজিশনে প্রবেশ করার সময় একবার নির্বাহের জন্য LaunchedEffect(Unit) ব্যবহার করুন বা নির্দিষ্ট মান পাস করুন যার পরিবর্তনে ইফেক্ট পুনরায় চালু হওয়া উচিত।
না, LaunchedEffect স্বয়ংক্রিয়ভাবে করুটিন বাতিল করে যখন composable কম্পোজিশন ছেড়ে যায়, মেমরি লিক প্রতিরোধ করে। তবে, যদি LaunchedEffect-এর ভিতরের করুটিন ক্লোজারের মাধ্যমে একটি Activity বা Context-এর রেফারেন্স ধরে রাখে, লিক সম্ভব — ViewModel-এ দীর্ঘস্থায়ী অপারেশনের জন্য viewModelScope ব্যবহার করুন।
LaunchedEffect কী বাঁধন সহ কম্পোজিশনে প্রবেশ করার সময় স্বয়ংক্রিয়ভাবে একটি করুটিন নির্বাহ করে। rememberCoroutineScope ম্যানুয়াল করুটিন চালু করার জন্য একটি স্কোপ প্রদান করে, উদাহরণস্বরূপ, onItemClick-এর প্রতিক্রিয়ায়। স্বয়ংক্রিয় সাইড ইফেক্টের জন্য LaunchedEffect ব্যবহার করুন এবং ব্যবহারকারীর ইভেন্টের ভিত্তিতে করুটিন চালু করার জন্য rememberCoroutineScope ব্যবহার করুন।
যদি LaunchedEffect কী একটি অস্থির ধরন হয় (উদাহরণস্বরূপ, var বা equals() ছাড়া ক্লাস), Compose চিনতে পারে না যে মান পরিবর্তিত হয়নি এবং প্রতিটি পুনঃকম্পোজিশনে ইফেক্ট পুনরায় চালু করবে। সমাধান: স্থিতিশীল ধরন (প্রিমিটিভ, স্ট্রিং, val ফিল্ড সহ data class) ব্যবহার করুন বা পরিবর্তনশীল মান remember-এ মোড়ান।
বাইরে থেকে LaunchedEffect বন্ধ করার কোনো সরাসরি উপায় নেই — নিয়ন্ত্রণ কী-এর মাধ্যমে পরিচালিত হয়। বর্তমান করুটিন বাতিল করতে কী পরিবর্তন করুন। যদি আপনার করুটিন জীবনচক্রের উপর সম্পূর্ণ নিয়ন্ত্রণের প্রয়োজন হয়, Job-এর সাথে rememberCoroutineScope ব্যবহার করুন এবং কোনো ইভেন্ট বা অবস্থা পরিবর্তনে ম্যানুয়ালি job.cancel() কল করুন।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন