Activity Result API: возврат данных из другой активности

На смену устаревшему startActivityForResult пришёл новый Activity Result API — более безопасный и удобный способ получения данных из другой активности. Если вы всё ещё используете старый подход с onActivityResult, пора обновить свои знания. В этом уроке вы узнаете, activity result api возврат данных из другой активности как работает, и напишете чистый, современный код на Kotlin.


Зачем отказались от startActivityForResult

Классический метод имел несколько проблем: обработка результата была привязана к конкретному Activity через переопределение onActivityResult, что приводило к путанице в больших проектах. Кроме того, код запуска и обработки оказывался разнесён по разным местам — вы вызывали startActivityForResult в одном месте, а результат приходил совсем в другом. С новым API запуск и получение результата находятся рядом, что делает код более читаемым и предсказуемым.


Ключевые компоненты Activity Result API

Новый механизм построен на трёх основных понятиях:

  • ActivityResultContract — описывает, какие входные данные принимает операция и какой тип результата возвращается. Библиотека предоставляет готовые контракты, например StartActivityForResult для запуска обычной Activity, GetContent для выбора файла, TakePicture для получения фото с камеры.
  • ActivityResultLauncher — создаётся из контракта и содержит метод launch(), который запускает целевую операцию.
  • Callback — передаётся при регистрации лаунчера и вызывается, когда операция завершается успешно.

Все эти компоненты регистрируются в рамках жизненного цикла Activity или Fragment с помощью специального метода registerForActivityResult.


Пошаговая реализация: отправка и приём данных

Рассмотрим самый частый сценарий — запуск второй активности, которая возвращает результат обратно. Первая активность отправляет запрос, вторая обрабатывает его и отдаёт ответ.


Шаг 1: Регистрация лаунчера в вызывающей активности

Объявите лаунчер до входа в активное состояние — обычно на уровне свойства класса. В колбэке вы получите код результата (RESULT_OK или RESULT_CANCELED) и Intent с данными.

private val startForResult = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
    if (result.resultCode == RESULT_OK) {
        val returnedValue = result.data?.getStringExtra("data_key")
        // обработайте полученные данные
    }
}

Контракт StartActivityForResult принимает Intent на вход и возвращает ActivityResult, содержащий code и data.


Шаг 2: Запуск второй активности

Создайте Intent, поместите в него любые нужные параметры, и вызовите launch(intent):

val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("request", "некоторые данные")
startForResult.launch(intent)

Никаких requestCode больше не требуется — каждый лаунчер работает изолированно.


Шаг 3: Возврат данных из второй активности

В SecondActivity сформируйте ответный Intent, присоедините к нему данные с помощью putExtra() и завершите активность с кодом RESULT_OK:

val resultIntent = Intent().apply {
    putExtra("data_key", "результат из второй активности")
}
setResult(RESULT_OK, resultIntent)
finish()

Если понадобится сообщить об отмене операции, можно использовать setResult(RESULT_CANCELED).


Создание собственного контракта

Библиотека даёт возможность написать свой контракт, если стандартных недостаточно. Допустим, вы хотите запустить вторую активность, которая возвращает объект User. Входной параметр — произвольный, а выходной тип может быть nullable.

class UserResultContract : ActivityResultContract<Intent, User?>() {
    override fun createIntent(context: Context, input: Intent): Intent = input

    override fun parseResult(resultCode: Int, intent: Intent?): User? {
        return if (resultCode == RESULT_OK) intent?.getSerializableExtra("user") as? User
        else null
    }
}

Использование контракта аналогично стандартному: registerForActivityResult(UserResultContract()) { user -> ... }.


Сравнение старого и нового подхода

Критерий startActivityForResult Activity Result API
Размещение кода Запуск и обработка разделены (один метод запускает, другой принимает) Запуск и обработка находятся вместе, в одной конструкции
Коллизии requestCode Необходимо вручную следить за уникальностью кодов Коды не используются, контракты работают изолированно
Проверка типов во время компиляции Отсутствует, данные извлекаются по ключам Контракты гарантируют типобезопасность возвращаемого результата
Обработка жизненного цикла Результат может быть потерян при пересоздании Activity API автоматически восстанавливает лаунчеры после поворота экрана

Запрос permissions через Activity Result API

Новый API унифицировал также работу с разрешениями. Вместо устаревшего requestPermissions используйте контракт RequestPermission:

private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
    if (isGranted) {
        // разрешение получено
    } else {
        // пользователь отказал
    }
}
// запуск запроса
requestPermissionLauncher.launch(Manifest.permission.CAMERA)

Для запроса нескольких разрешений одновременно применяется контракт RequestMultiplePermissions, возвращающий Map<String, Boolean>.


Что важно запомнить

Новый activity result api возврат данных из другой активности делает обмен информацией между экранами простым и надёжным. Главные правила, которые следует соблюдать: регистрируйте лаунчер до вызова onCreate (или внутри него), не забывайте вызывать launch() с Intent, а в принимающей стороне явно завершайте активность через finish() после установки результата. Освоив этот API, вы избавитесь от путаницы с requestCode и получите типобезопасный код, который легко читать и поддерживать. Используйте его в своих следующих проектах — старые методы скоро окончательно перейдут в разряд легаси.