লাইফসাইকেল-অ্যাওয়ার কম্পোনেন্ট সহ Kotlin কোরুটিন ব্যবহার করা

Kotlin coroutines এমন একটি API প্রদান করে যা আপনাকে অ্যাসিঙ্ক্রোনাস কোড লিখতে দেয়। Kotlin করোটিনের সাহায্যে, আপনি একটি CoroutineScope নির্ধারণ করতে পারবেন, যা আপনার করোটিন কখন রান করা উচিত তা ম্যানেজ করতে সাহায্য করে। প্রতিটি অ্যাসিঙ্ক্রোনাস অপারেশন একটি নির্দিষ্ট স্কোপের মধ্যে রান করে।

লাইফসাইকেল-অ্যাওয়ার কম্পোনেন্ট আপনার অ্যাপের লজিক্যাল স্কোপের জন্য কোরাউটিনের ক্ষেত্রে ফার্স্ট-ক্লাস সহায়তা প্রদান করে। এই ডকুমেন্টটি লাইফসাইকেল-অ্যাওয়ার কম্পোনেন্টের সাথে কোরাউটিন কীভাবে কার্যকরভাবে ব্যবহার করতে হয় তা ব্যাখ্যা করে।

ডিপেন্ডেন্সি যোগ করা

এই বিষয়ে বর্ণিত বিল্ট-ইন কোরুটিন স্কোপ Lifecycle API-তে থাকে। এইসব স্কোপ ব্যবহার করার সময় উপযুক্ত ডিপেন্ডেন্সি যোগ করতে ভুলবেন না।

  • Compose-এ ViewModel ইউটিলিটির জন্য, implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version") ব্যবহার করুন।
  • Compose-এ লাইফসাইকেল ইউটিলিটির জন্য, implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version") ব্যবহার করুন।

লাইফসাইকেল-অ্যাওয়ার কোরাউটিন স্কোপ

Compose ও Lifecycle লাইব্রেরি নিম্নলিখিত বিল্ট-ইন স্কোপ প্রদান করে যা আপনি নিজের অ্যাপে ব্যবহার করতে পারবেন।

ViewModelScope

আপনার অ্যাপের প্রতিটি ViewModel-এর জন্য একটি ViewModelScope নির্ধারণ করা হয়। এই স্কোপে লঞ্চ করা যেকোনও করুটিন অটোমেটিক বাতিল হয়ে যায় যদি ViewModel মুছে দেওয়া হয়। ViewModel অ্যাক্টিভ থাকলেই শুধুমাত্র করতে হবে এমন কাজ থাকলে, সেক্ষেত্রে কোরাউটিন কাজে লাগে। যেমন, আপনি যদি কোনও লেআউটের জন্য কিছু ডেটা কম্পিউট করেন, তাহলে আপনাকে ViewModel-এ কাজের স্কোপ করতে হবে যাতে ViewModel মুছে ফেলা হলে, রিসোর্স খরচ করা এড়াতে কাজটি অটোমেটিক বাতিল হয়ে যায়।

আপনি ViewModel-এর CoroutineScope, ViewModel-এর viewModelScope প্রপার্টির মাধ্যমে অ্যাক্সেস করতে পারবেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
}

আরও উন্নত ব্যবহারের ক্ষেত্রে, ডিফল্ট viewModelScope পরিবর্তন করতে, আপনি সরাসরি ViewModel-এর কনস্ট্রাক্টরে কাস্টম CoroutineScope পাস করতে পারবেন। এই পদ্ধতি আরও বেশি নিয়ন্ত্রণ ও সুবিধা প্রদান করে, বিশেষ করে এইসব ক্ষেত্রে:

  • টেস্টিং: এটি আপনাকে TestScope ইনজেক্ট করতে দেয়, এর ফলে ইউনিট টেস্টে সময় নিয়ন্ত্রণ করা এবং করুটিনের আচরণ যাচাই করা সহজ হয়ে যায়।

  • কাস্টম কনফিগারেশন: ViewModel কাজ শুরু করার আগেই আপনি নির্দিষ্ট CoroutineDispatcher (যেমন, ভারী কম্পিউটেশনের জন্য Dispatchers.Default) অথবা কাস্টম CoroutineExceptionHandler দিয়ে স্কোপ কনফিগার করতে পারেন।

কম্পোজিশন-বাউন্ড স্কোপ

অ্যানিমেশন, নেটওয়ার্ক কল বা টাইমারের মতো পার্শ্ব প্রতিক্রিয়া অবশ্যই কম্পোজ করার উপযুক্ত আইটেমের লাইফসাইকেলে স্কোপ করতে হবে। এইভাবে, কোনও কম্পোজ করার উপযুক্ত আইটেম স্ক্রিন থেকে বেরিয়ে গেলে (কম্পোজিশন থেকে বেরিয়ে গেলে), মেমরি লিক আটকানোর জন্য চলমান যেকোনও কোরাউটিন অটোমেটিক বাতিল হয়ে যায়।

Composition স্কোপিং ডিক্লেয়ার করার জন্য Compose LaunchedEffect API প্রদান করে।

LaunchedEffect একটি CoroutineScope তৈরি করে যা আপনাকে সাসপেন্ড ফাংশন চালাতে দেয়। স্কোপটি কম্পোজ করার উপযুক্ত আইটেমের কম্পোজিশন লাইফসাইকেলের সাথে যুক্ত থাকে, হোস্ট অ্যাক্টিভিটির লাইফসাইকেলের সাথে নয়।

  • এন্টার: কম্পোজিশনে কম্পোজ করার উপযুক্ত আইটেম যোগ করা হলে কোরাউটিন শুরু হয়।
  • বেরিয়ে আসা: কম্পোজ করার উপযুক্ত আইটেম কম্পোজিশন থেকে বেরিয়ে এলে কোরাউটিন বাতিল হয়ে যায়।
  • আবার লঞ্চ করা: LaunchedEffect-এ পাস করা কোনও কী পরিবর্তন হলে, আগে থেকে থাকা করুটিন বাতিল হয়ে যায় এবং নতুন একটি লঞ্চ করা হয়।

নিম্নলিখিত উদাহরণ থেকে কীভাবে LaunchedEffect ব্যবহার করে পালস করা অ্যানিমেশন তৈরি করা যায় তা জানুন। করুটিনটি কম্পোজিশনে কম্পোজ করার উপযুক্ত আইটেমের উপস্থিতি সাথে যুক্ত থাকে এবং কনফিগারেশনে হওয়া পরিবর্তন অনুযায়ী কাজ করে:

// Allow the pulse rate to be configured, so it can be sped up if the user is running
// out of time
var pulseRateMs by remember { mutableLongStateOf(3000L) }
val alpha = remember { Animatable(1f) }
LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes
    while (isActive) {
        delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user
        alpha.animateTo(0f)
        alpha.animateTo(1f)
    }
}

LaunchedEffect সম্পর্কে আরও জানতে, Compose-এ সাইড-ইফেক্ট দেখুন।

লাইফসাইকেল-সচেতন ফ্লো সংগ্রহ

Jetpack Compose-এ নিরাপদে ফ্লো সংগ্রহ করতে, collectAsStateWithLifecycle API ব্যবহার করুন। এই একটি ফাংশন Flow থেকে Compose State অবজেক্টে কনভার্ট করে এবং আপনার জন্য অটোমেটিক লাইফসাইকেল সাবস্ক্রিপশন ম্যানেজ করে। ডিফল্ট হিসেবে, লাইফসাইকেল STARTED হলে সংগ্রহ শুরু হয় এবং লাইফসাইকেল STOPPED হলে বন্ধ হয়ে যায়। এই ডিফল্ট বিহেভিয়ার ওভাররাইড করতে, আপনি যে লাইফসাইকেল পদ্ধতি চান সেটি সহ minActiveState প্যারামিটার পাস করুন, যেমন Lifecycle.State.RESUMED।

নিম্নলিখিত উদাহরণ থেকে বোঝা যাবে যে কীভাবে কোনও কম্পোজ করার উপযুক্ত আইটেমে ViewModel-এর StateFlow সংগ্রহ করা যায়:

@Composable
private fun ConversationScreen(
    conversationViewModel: ConversationViewModel = viewModel()
) {

    val messages by conversationViewModel.messages.collectAsStateWithLifecycle()

    ConversationScreen(
        messages = messages,
        onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) }
    )
}

@Composable
private fun ConversationScreen(
    messages: List<Message>,
    onSendMessage: (Message) -> Unit
) {

    MessagesList(messages, onSendMessage)
    /* ... */
}

একাধিক ফ্লো সমান্তরালভাবে সংগ্রহ করা

Compose-এ, একাধিক স্টেট ভেরিয়েবল ঘোষণা করে আপনি সমান্তরালভাবে একাধিক ফ্লো সংগ্রহ করতে পারবেন। collectAsStateWithLifecycle যেহেতু নিজের অন্তর্নিহিত স্কোপ ম্যানেজ করে, তাই প্যারালাল সংগ্রহ অটোমেটিক ম্যানেজ করা হয়:

@Composable
fun DashboardScreen(viewModel: DashboardViewModel = viewModel()) {
    // Both flows are collected safely in parallel and will emit updates when either changes, the composables will recompose
    val userData by viewModel.userFlow.collectAsStateWithLifecycle()
    val feedData by viewModel.feedFlow.collectAsStateWithLifecycle()

    // ...
}

Flow ব্যবহার করে অ্যাসিঙ্ক্রোনাস পদ্ধতিতে মান গণনা করা

অ্যাসিঙ্ক্রোনাস পদ্ধতিতে ভ্যালু গণনা করার প্রয়োজন হলে, stateIn অপারেটরের সাথে StateFlow ব্যবহার করুন।

নিচের স্নিপেটে একটি স্ট্যান্ডার্ড Flow ব্যবহার করা হয়েছে যা StateFlow-এ কনভার্ট করা হয়েছে। কনফিগারেশন পরিবর্তন ম্যানেজ করার জন্য UI অদৃশ্য হয়ে যাওয়ার পরে WhileSubscribed(5000) প্যারামিটার পাঁচ সেকেন্ডের জন্য সাবস্ক্রিপশন চালু রাখে।

val uiState: StateFlow<Result> = flow {
    emit(repository.fetchData())
}
.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5_000),
    initialValue = Result.Loading
)

সংগ্রহ করা ভ্যালুকে Compose State-এ কনভার্ট করতে collectAsStateWithLifecycle ব্যবহার করুন, যাতে ডেটা পরিবর্তন হলে আপনার UI রিঅ্যাক্টিভভাবে আপডেট হতে পারে।

স্টেট সম্পর্কে আরও জানতে, স্টেট ও Jetpack Compose দেখুন।

অতিরিক্ত রিসোর্স

কন্টেন্ট দেখা

নমুনা