ActivityResultLauncher: що це таке і як використовувати

Автор: IT Sectr Опубліковано: 2026-06-10 Час читання: 9 хв

ActivityResultLauncher — це компонент Android Activity Result API, представлений у версії Activity 1.2.0 бібліотеки androidx.activity. Він замінює застарілі методи startActivityForResult і onActivityResult, які були частиною Android SDK з моменту його створення. За даними Android Developers (2024), новий API усуває проблеми жорсткого зв’язку з Activity та відсутності типобезпеки. ActivityResultLauncher реєструється заздалегідь і використовує Contract для суворої типізації вхідних і вихідних даних.

Головне

  • ActivityResultLauncher — новий API для отримання результату від Activity замість застарілого startActivityForResult
  • Contract — об’єкт, що визначає тип вхідних і вихідних даних для конкретного сценарію
  • Реєстрація виконується через registerForActivityResult до виклику launch
  • Callback викликається після завершення цільової Activity з результатом
  • API доступний для Activity, Fragment і Compose починаючи з Activity 1.2.0

Що таке ActivityResultLauncher

ActivityResultLauncher — це клас з пакета androidx.activity.result, який надає типобезпечний механізм запуску Activity та отримання результату. Launcher створюється через метод registerForActivityResult, який приймає два параметри: Contract (описує вхідні та вихідні типи) і ActivityResultCallback (обробник результату). Після реєстрації launcher готовий до виклику через метод launch.

Ключова відмінність від старого API — розділення реєстрації та запуску. Реєстрація виконується на етапі ініціалізації (Activity.onCreate або Fragment.onCreate), колбек зв’язується з launcher один раз і гарантовано спрацьовує при поверненні результату. Це усуває проблему, коли onActivityResult спрацьовував у неочікуваному порядку або на знищеній Activity.

ActivityResultLauncher підтримує всі сценарії, які раніше оброблялися через onActivityResult: запуск камери, галереї, запит контактів, дозволів і кастомних Activity. Крім того, API розширюваний: розробник може створювати власні Contract для специфічних сценаріїв обміну даними між Activity.

Чому Activity Result API замінив startActivityForResult

startActivityForResult був частиною Android SDK з версії API Level 1 (2008) і залишався основним способом отримання результату від Activity протягом понад 12 років. Однак цей метод мав фундаментальні недоліки, які Google усунула в Activity Result API. Розглянемо основні проблеми та те, як новий API їх вирішує.

Проблема 1: жорсткий зв’язок з Activity

Метод startActivityForResult прив’язаний до Activity і Fragment через requestCode — довільне ціле число, яке передається в onActivityResult. Розробник вручну зіставляв код із запущеною операцією, що призводило до помилок при перевикористанні коду та успадкуванні. ActivityResultLauncher усуває requestCode повністю: колбек прив’язується до конкретного launcher на етапі реєстрації та викликається тільки для нього.

Проблема 2: втрата результату при повороті екрана

При конфігураційних змінах (поворот екрана, зміна мови) Activity перестворювалася, і onActivityResult міг не спрацювати — колбек втрачався. Activity Result API автоматично зберігає та відновлює стан launcher через SavedStateRegistry, що гарантує отримання результату навіть після перестворення Activity.

Проблема 3: відсутність типобезпеки

Старий API передавав результат через Intent з Bundle, де ключі та типи даних не перевірялися компілятором. Activity Result API використовує Contract — generic-інтерфейс, який визначає тип вхідних даних (I) і тип результату (O). Помилки невідповідності типів виявляються на етапі компіляції, а не в рантаймі.

ХарактеристикаstartActivityForResultActivityResultLauncher
RequestCodeПотрібне ручне керуванняАвтоматичний, не потрібен
ТипобезпекаНемаєGeneric Contract
Збереження при поворотіВтрачаєтьсяSavedStateRegistry
Мінімальний APIAPI Level 1Activity 1.2.0
Використання в ComposeНе підтримуєтьсяrememberLauncherForActivityResult

Основні контракти Activity Result API

Contract — це інтерфейс ActivityResultContract<I, O>, який визначає, як запускати Activity і як інтерпретувати результат. Google надає набір вбудованих контрактів для типових сценаріїв, що покривають більшість потреб розробника.

StartIntentSenderForResult

StartIntentSenderForResult — базовий контракт для запуску IntentSender. Використовується в системних сценаріях, наприклад при авторизації через Google Sign-In або при оплаті через Google Pay. Вхідний параметр — PendingIntent, вихідний — ActivityResult з кодом і Intent.

RequestMultiplePermissions

RequestMultiplePermissions — контракт для запиту кількох дозволів одночасно в Android 6.0+. Вхідний параметр — масив String з назвами дозволів, вихідний — Map<String, Boolean> з результатом кожного запиту. Раніше для цього потрібен був ручний розбір в onRequestPermissionsResult із зіставленням кодів запитів.

TakePicture і TakeVideo

TakePicture — контракт для зйомки фото через системну камеру. Вхід — Uri, куди зберегти знімок, вихід — Boolean (успіх). TakeVideo аналогічно працює з відео. Ці контракти замінюють застарілий MediaStore.ACTION_IMAGE_CAPTURE з нестабільною поведінкою на різних пристроях.

GetContent і OpenDocument

GetContent — контракт для вибору контенту через системний picker. Вхід — MIME-тип (наприклад image/*), вихід — Uri вибраного файлу. OpenDocument відрізняється підтримкою множинного вибору та фільтрацією за типами документів. Обидва контракти працюють через SAF (Storage Access Framework).

CreateDocument і OpenDocumentTree

CreateDocument — контракт для створення нового документа через системний діалог. Користувач вибирає ім’я та папку, система повертає Uri для запису. OpenDocumentTree надає доступ до цілої директорії — користувач вибирає папку, і застосунок отримує tree-uri для читання та запису всіх файлів всередині.

Використання в Activity і Fragment

Базовий патерн використання ActivityResultLauncher в класичному Android складається з двох кроків: реєстрація через registerForActivityResult на етапі ініціалізації та виклик launch у відповідь на дію користувача. Розглянемо типовий приклад вибору зображення з галереї.

Реєстрація та запуск в Activity

Реєструйте launcher в onCreate Activity — це гарантує, що колбек буде готовий до будь-якого можливого виклику. Ніколи не реєструйте launcher безпосередньо перед запуском — це порушує контракт API і може призвести до втрати результату при перестворенні Activity.

kotlin
class MainActivity : AppCompatActivity() {
    private val pickImageLauncher =
        registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
            uri?.let { binding.imageView.setImageURI(it) }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pickImageLauncher.launch("image/*")
    }
}

Використання в Fragment

У Fragment реєстрація виконується в onCreate, onAttach або ініціалізації в onCreateView. FragmentActivity передає launcher через батьківську Activity, тому результат обробляється всередині Fragment, а не в Activity. Це покращує інкапсуляцію порівняно з onActivityResult, де всі результати з усіх Fragment збиралися в одному методі Activity.

kotlin
class ProfileFragment : Fragment() {
    private val cameraLauncher =
        registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
            if (success) { updateProfilePhoto() }
        }

    fun takePhoto(photoUri: Uri) {
        cameraLauncher.launch(photoUri)
    }
}

ActivityResultLauncher в Jetpack Compose

Jetpack Compose надає спеціальний composable-функціонал для Activity Result API — rememberLauncherForActivityResult. На відміну від класичного підходу, в Compose launcher створюється як об’єкт, прив’язаний до життєвого циклу composable через remember. Це дозволяє використовувати Activity Result API повністю в декларативному стилі без прямого доступу до Activity або Fragment.

rememberLauncherForActivityResult

rememberLauncherForActivityResult приймає Contract і колбек, повертаючи ActivityResultLauncher. Launcher зберігається при рекомпозиції та автоматично очищається при виході з композиції. Виклик launch відбувається у відповідь на подію — наприклад, натискання кнопки або зміну стану.

kotlin
@Composable
fun PhotoPicker() {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        ActivityResultContracts.GetContent()
    ) { uri -> handleImage(uri) }

    Button(onClick = { launcher.launch("image/*") }) {
        Text("Вибрати фото")
    }
}

Обробка дозволів в Compose

Запит дозволів в Compose також виконується через rememberLauncherForActivityResult з контрактом RequestPermission або RequestMultiplePermissions. Google рекомендує використовувати accompanist-permissions, але під капотом він також використовує Activity Result API. Для контролю стану дозволів зручно зберігати статус в remember або ViewModel.

Типові помилки та best practices

Activity Result API усунув багато проблем старого підходу, але його неправильне використання може призвести до нових видів помилок. Розглянемо найпоширеніші проблеми та способи їх уникнення.

Помилка: реєстрація всередині лямбди або корутини

Реєстрація launcher повинна виконуватися при ініціалізації компонента — в onCreate Activity або ініціалізаторі Fragment. Якщо реєструвати launcher всередині лямбди, колбека або корутини, при перестворенні Activity реєстрація може бути виконана повторно, і старий launcher втратить зв’язок з результатом.

Помилка: реєстрація кількох launcher з однаковим ключем

Кожен launcher отримує унікальний ключ для збереження стану. Якщо зареєструвати два launcher з однаковим Contract в одному компоненті, SavedStateRegistry може перезаписати стан одного іншим. Android Studio попереджає про це через lint-правило UnnecessaryRegisterForActivityResult, але краще контролювати унікальність вручну.

Best Practice: завжди обробляти null-результат

Користувач може скасувати дію — натиснути системну кнопку назад, згорнути застосунок або переключитися на інший застосунок. У цьому випадку колбек отримає null або ActivityResult з RESULT_CANCELED. Завжди перевіряйте результат на null перед використанням, щоб уникнути NullPointerException.

Best Practice: кастомні Contract для перевикористовуваної логіки

Якщо ваш застосунок часто запускає однотипні сценарії — наприклад, вибір контакту з поверненням імені та телефону — створіть власний Contract. Це покращує читаність коду та дозволяє централізовано змінювати логіку запуску та обробки результату.

kotlin
class PickContactContract : ActivityResultContract<Void, ContactData?>() {
    override fun createIntent(context: Context, input: Void?) =
        Intent(Intent.ACTION_PICK).setType(ContactsContract.Contacts.CONTENT_TYPE)

    override fun parseResult(resultCode: Int, intent: Intent?) =
        intent?.data?.let { queryContact(it) }
}

Часто задавані питання

Чи можна використовувати ActivityResultLauncher у ViewModel?

Ні — ActivityResultLauncher вимагає контекст Activity або Fragment для реєстрації. Використовуйте ViewModel тільки для зберігання стану, а launcher створюйте в Activity або Fragment і передавайте результат у ViewModel.

Який мінімальний SDK потрібен для Activity Result API?

Activity Result API доступний починаючи з бібліотеки activity-ktx 1.2.0. Мінімальний SDK — API Level 14 (Android 4.0), але більшість контрактів працюють тільки на API Level 19+.

Що станеться, якщо викликати launch двічі до отримання результату?

Повторний виклик launch до завершення першої операції буде проігноровано. Activity Result API не підтримує паралельні запуски — дочекайтеся колбека від першої операції перед новим викликом.

Чим замінити onActivityResult у старому коді?

Міграція виконується заміною виклику startActivityForResult на registerForActivityResult з відповідним Contract. Видаліть onActivityResult і обробляйте результат у колбеку launcher. Google надає міграційний гайд у документації Android Developers.

Чи працює ActivityResultLauncher з бібліотеками на кшталт ML Kit або Barcode Scanner?

Так, багато бібліотек підтримують інтеграцію через ActivityResultContracts. Наприклад, ML Kit Barcode Scanner використовує StartIntentSenderForResult для запуску сканера. Перевірте документацію конкретної бібліотеки.

Підсумки

  • ActivityResultLauncher — сучасний типобезпечний API замість застарілого startActivityForResult
  • Contract визначає тип вхідних і вихідних даних, усуваючи ручне зіставлення requestCode
  • Реєстрація виконується на етапі ініціалізації, результат гарантовано доставляється через колбек
  • Вбудовані контракти покривають камеру, галерею, дозволи, документи та контакти
  • Jetpack Compose використовує rememberLauncherForActivityResult для роботи з API
  • Кастомні Contract дозволяють перевикористовувати логіку запуску між компонентами
  • API зберігає стан при конфігураційних змінах через SavedStateRegistry

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також