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 — 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 — уговор за избор садржаја кроз системски бирач. Улаз — 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође