Як запобігти багаторазовим натисканням в Android Jetpack Compose і до чого тут Lifecycle

Думаю, всі розробники стикалися з проблемою багаторазового натискання кнопок користувачем. Особливий біль приносили кнопки, до яких була прикріплена навігація.

Раніше, повторне натискання просто клало застосунок, так як процес уже запустився, і ще раз навігуватись із поточного екрану було не можливо.

Зараз саме цю проблему пофіксили, і апка не вилітає, проте екран створюється кілька разів (по кількості натискань) і потрапляє в стек навігації, що теж не є добре.

Звісно, розробники вигадали купу різних способів, щоб це обійти. І затримка часу, коли на деякий час після натискання кнопка блокується. Блокування кнопки після натискання, обертання навігації в LaunchEffect, перевірка currentDestination та інші цікаві способи.

Це все працювало, але це все "милиці", якими підпирали проблему.

І ось, не знаю коли саме знайшлось це рішення, але воно є:

Це функція dropUnlessResumed()

Button(
    onClick =
        dropUnlessResumed {
            // Run on clicks only when the lifecycle is at least RESUMED.
            // Example: navController.navigate("next_screen")
        },
) {
    Text(text = "Navigate to next screen")
}

Ось так вона виглядає:

@Composable
fun dropUnlessResumed(
    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
    block: () -> Unit
): () -> Unit

Цей функція блокує виконання коду, що в ній розміщено, якщо

lifecycleState != State.RESUMED

От ми і підійшли до моменту, коли потрібно розкрити роль Lifesycle у всьому цьому.

Деякі розробники сумніваються в правильності підходу використання функції dropUnlessResumed(), хоча саме такий спосіб її використання вказаний у документації. Аргументують це тим, що стан життєвого циклу можуть змінити і інші чинники.

То ж, давайте трохи розберемось із цим станом

Екрани написані з допомогою compose мають свій життєвий цикл:

  • CREATED

  • STARTED

  • RESUMED

  • DESTROYED

Доступний для користування екран, має стан RESUMED. В момент натискання кнопки і виклику навігації, система генерує подію, яка приводить до зміни стану за таким принципом:

ON_CREATE, ON_STOP -> return State.CREATED
ON_START, ON_PAUSE -> return State.STARTED
ON_RESUME -> return State.RESUMED
ON_DESTROY -> return State.DESTROYED
ON_ANY -> {}

В нашому випадку це ON_PAUSE і STARTED, відповідно.

Оскільки STARTED не RESUMED - блок коду, що запускає навігацію, який ми помістили у фігурні дужки методу dropUnlessResumed не буде виконано, скільки б разів не тиснули на кнопку.

Тепер щодо зміни стану з інших причин. Функція блокує, а не запускає виконання коду. То ж, якщо стан життєвого циклу зміниться через інші події, дія кнопки буде заблокована. Але ж нам це і потрібно, чи не так?

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Євгеній Маслак
Євгеній Маслак@maslak

11Прочитань
0Автори
0Читачі
На Друкарні з 15 серпня

Більше від автора

  • Нова навігація в Jetpack compose на android

    Від 4 вересня 2024 року, коли вийшло оновлення 2.8.0, робити навігацію з передачею параметрів між екранами в jetpack compose стало набагато простіше. А також зручніше і зрозуміліше

    Теми цього довгочиту:

    Android

Вам також сподобається

  • Google Play Services - оновлення без оновлень?

    Побутує думка, нібито смартфони на Android досить швидко перетворюються на гарбуз через коротку підтримку оновленнями ПЗ від виробника. Тут я спробую показати протилежне.

    Теми цього довгочиту:

    Android

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається

  • Google Play Services - оновлення без оновлень?

    Побутує думка, нібито смартфони на Android досить швидко перетворюються на гарбуз через коротку підтримку оновленнями ПЗ від виробника. Тут я спробую показати протилежне.

    Теми цього довгочиту:

    Android