استفاده از روال‌های مشترک Kotlin با عناصر آگاه از چرخه حیات

روال‌های مشترک Kotlin میانای برنامه‌سازی کاربردی‌ای ارائه می‌دهد که به شما امکان می‌دهد کد ناهمگام بنویسید. با روال‌های هم‌زمان Kotlin، می‌توانید CoroutineScope را تعریف کنید که به شما کمک می‌کند زمان اجرای روال‌های هم‌زمان را مدیریت کنید. هر عملیات ناهمگام در محدوده خاصی اجرا می‌شود.

مؤلفه‌های آگاه از چرخه حیات پشتیبانی درجه یکی برای هم‌روال‌ها برای دامنه‌های منطقی در برنامه‌تان ارائه می‌دهند. این سند توضیح می‌دهد که چگونه از هم‌روال‌ها به‌طور مؤثر با مؤلفه‌های آگاه از چرخه حیات استفاده کنید.

افزودن وابستگی‌ها

دامنه‌های داخلی coroutine که در این موضوع شرح داده شده‌اند در Lifecycle API قرار دارند. هنگام استفاده از این گستره‌ها، حتماً وابستگی‌های مناسب را اضافه کنید.

  • برای برنامه‌های کمکی ViewModel در Compose، از implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version") استفاده کنید.
  • برای برنامه‌های کمکی «چرخه حیات» در Compose، از implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version") استفاده کنید.

دامنه‌های هم‌روال آگاه از چرخه حیات

Compose و کتابخانه‌های Lifecycle دامنه‌های داخلی زیر را ارائه می‌دهند که می‌توانید در برنامه‌تان استفاده کنید.

ViewModelScope

یک ViewModelScope برای هر ViewModel در برنامه‌تان تعریف شده است. هر روال هم‌زمان که در این محدوده راه‌اندازی شود، درصورت پاک شدن ViewModel به‌طور خودکار لغو می‌شود. در اینجا، روال‌های هم‌زمان برای زمانی مفید هستند که کاری دارید که باید انجام شود فقط اگر ViewModel فعال باشد. برای مثال، اگر درحال محاسبه برخی داده‌ها برای چیدمان هستید، باید دامنه کار را به ViewModel محدود کنید تا اگر ViewModel پاک شد، کار به‌طور خودکار لغو شود و از مصرف منابع جلوگیری شود.

می‌توانید ازطریق ویژگی viewModelScope مربوط به ViewModel به CoroutineScope مربوط به ViewModel دسترسی پیدا کنید، همان‌طور که در مثال زیر نشان داده شده است:

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

برای موارد استفاده پیشرفته‌تر، می‌توانید یک CoroutineScope سفارشی را مستقیماً به سازنده ViewModel ارسال کنید تا جایگزین viewModelScope پیش‌فرض شود. این رویکرد کنترل و انعطاف‌پذیری بیشتری ارائه می‌دهد، به‌ویژه برای:

  • آزمایش: به شما امکان می‌دهد TestScope را تزریق کنید، که کنترل زمان و درستی‌سنجی رفتار هم‌روال را در آزمایش‌های واحد آسان‌تر می‌کند.

  • پیکربندی سفارشی: می‌توانید دامنه را با یک CoroutineDispatcher خاص (مانند Dispatchers.Default برای محاسبات سنگین) یا یک CoroutineExceptionHandler سفارشی قبل‌از اینکه ViewModel حتی کار خود را شروع کند، پیکربندی کنید.

دامنه‌های محدود به ترکیب‌بندی

عوارض جانبی مانند پویانمایی‌ها، تماس‌های شبکه، یا زمان‌سنج‌ها باید به چرخه حیات composable محدود شوند. به‌این‌ترتیب، وقتی یک composable صفحه را ترک می‌کند (از ترکیب‌بندی خارج می‌شود)، هر coroutine درحال اجرا به‌طور خودکار لغو می‌شود تا از نشت حافظه جلوگیری شود.

Compose برای مدیریت کردن محدوده «ترکیب‌بندی» به‌صورت اعلانی، LaunchedEffect API را ارائه می‌دهد.

‫LaunchedEffect یک CoroutineScope ایجاد می‌کند که به شما امکان می‌دهد توابع تعلیق را اجرا کنید. این دامنه به چرخه حیات «ترکیب‌بندی» composable گره خورده است، نه «چرخه حیات» «فعالیت» میزبان.

  • ورود: وقتی که ترکیب‌پذیر وارد ترکیب‌بندی می‌شود، کوروتین شروع می‌شود.
  • خروج: وقتی composable ترکیب‌بندی را ترک می‌کند، coroutine لغو می‌شود.
  • راه‌اندازی مجدد: اگر هر کلیدی که به 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، ارسال کنید.

مثال زیر نشان می‌دهد که چگونه می‌توان StateFlow ViewModel را در یک composable جمع‌آوری کرد:

@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()

    // ...
}

محاسبه مقادیر به‌صورت ناهمگام بااستفاده از Flows

وقتی لازم است مقادیر را به‌صورت ناهمگام محاسبه کنید، از StateFlow با عملگر stateIn استفاده کنید.

قطعه کد زیر از Flow استاندارد تبدیل‌شده به StateFlow استفاده می‌کند. پارامتر WhileSubscribed(5000) اشتراک را برای پنج ثانیه پس‌از ناپدید شدن میانای کاربر فعال نگه می‌دارد تا تغییرات پیکربندی را مدیریت کند.

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

از collectAsStateWithLifecycle برای تبدیل مقادیر جمع‌آوری‌شده به Compose State استفاده کنید تا واسط کاربر شما بتواند هر زمان که داده‌ها تغییر می‌کند به‌طور واکنشی به‌روز شود.

برای اطلاعات بیشتر درباره حالت، به حالت و Jetpack Compose مراجعه کنید.

منابع بیشتر

محتوا را مشاهده می‌کند

نمونه‌ها