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 с результатом каждого запроса. Ранее для этого требовался ручной разбор в 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("Choose Photo")
    }
}

Обработка разрешений в Compose

Запрос разрешений в Compose также выполняется через rememberLauncherForActivityResult с контрактом RequestPermission или RequestMultiplePermissions. Google recommends использовать 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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