На смену устаревшему 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 и получите типобезопасный код, который легко читать и поддерживать. Используйте его в своих следующих проектах — старые методы скоро окончательно перейдут в разряд легаси.