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), callback се свързва с 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: callback се свързва с конкретен launcher във фазата на регистрация и се извиква само за него.

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

При конфигурационни промени (завъртане на екрана, смяна на езика) Activity се пресъздаваше и onActivityResult можеше да не се задейства — callback се губеше. Activity Result API автоматично запазва и възстановява състоянието на launcher чрез SavedStateRegistry, което гарантира получаване на резултата дори след пресъздаване на Activity.

Проблем 3: липса на типова безопасност

Старият API предаваше резултата чрез Intent с Bundle, където ключовете и типовете данни не се проверяваха от компилатора. Activity Result API използва Contract — генеричен интерфейс, който определя типа на входните данни (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 е договорът за избор на съдържание чрез системния избирач. Вход — 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 — това гарантира, че callback ще бъде готов преди всяко възможно извикване. Никога не регистрирайте 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 и callback, връщайки 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.

Типични грешки и най-добри практики

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

Грешка: регистрация вътре в ламбда или корутина

Регистрацията на launcher трябва да се извърши при инициализация на компонента — в onCreate на Activity или инициализатора на Fragment. Ако регистрирате launcher вътре в ламбда, callback или корутина, при пресъздаване на Activity регистрацията може да бъде извършена отново и старият launcher ще загуби връзка с резултата.

Грешка: регистрация на няколко launcher с еднакъв ключ

Всеки launcher получава уникален ключ за запазване на състояние. Ако регистрирате два launcher с еднакъв Contract в един компонент, SavedStateRegistry може да презапише състоянието на единия с другия. Android Studio предупреждава за това чрез lint правилото UnnecessaryRegisterForActivityResult, но е по-добре да контролирате уникалността ръчно.

Най-добра практика: винаги обработвайте null резултат

Потребителят може да отмени действието — да натисне системния бутон за назад, да минимизира приложението или да превключи към друго приложение. В този случай callback ще получи null или ActivityResult с RESULT_CANCELED. Винаги проверявайте резултата за null преди използване, за да избегнете NullPointerException.

Най-добра практика: персонализирани 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 не поддържа паралелни стартирания — изчакайте callback от първата операция преди ново извикване.

С какво да заменим onActivityResult в стар код?

Миграцията се извършва чрез замяна на извикването на startActivityForResult с registerForActivityResult със съответния Contract. Премахнете onActivityResult и обработвайте резултата в callback на launcher. Google предоставя миграционно ръководство в документацията на Android Developers.

Работи ли ActivityResultLauncher с библиотеки като ML Kit или Barcode Scanner?

Да, много библиотеки поддържат интеграция чрез ActivityResultContracts. Например ML Kit Barcode Scanner използва StartIntentSenderForResult за стартиране на скенера. Проверете документацията на конкретната библиотека.

Обобщение

  • ActivityResultLauncher — модерен типобезопасен API вместо остарелия startActivityForResult
  • Contract определя типа на входните и изходните данни, премахвайки ръчното съпоставяне на requestCode
  • Регистрация се извършва във фазата на инициализация, резултатът се доставя гарантирано чрез callback
  • Вградени договори покриват камера, галерия, разрешения, документи и контакти
  • Jetpack Compose използва rememberLauncherForActivityResult за работа с API
  • Персонализирани Contract позволяват многократно използване на логиката за стартиране между компоненти
  • API запазва състояние при конфигурационни промени чрез SavedStateRegistry

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също