ActivityResultLauncher je komponenta Android Activity Result API, představená ve verzi Activity 1.2.0 knihovny androidx.activity. Nahrazuje zastaralé metody startActivityForResult a onActivityResult, které byly součástí Android SDK od jeho vzniku. Podle Android Developers (2024) nové API odstraňuje problémy těsného propojení s Activity a chybějící typové bezpečnosti. ActivityResultLauncher se registruje předem a používá Contract pro striktní typování vstupních a výstupních dat.
Hlavní body
ActivityResultLauncher je třída z balíčku androidx.activity.result, která poskytuje typově bezpečný mechanismus pro spouštění Activity a získávání výsledku. Launcher je vytvořen pomocí metody registerForActivityResult, která přijímá dva parametry: Contract (popisuje vstupní a výstupní typy) a ActivityResultCallback (zpracovatel výsledku). Po registraci je launcher připraven k volání pomocí metody launch.
Klíčový rozdíl oproti starému API je oddělení registrace a spouštění. Registrace probíhá ve fázi inicializace (Activity.onCreate nebo Fragment.onCreate), callback je navázán na launcher jednou a garantovaně se aktivuje při vrácení výsledku. To odstraňuje problém, kdy se onActivityResult aktivoval v neočekávaném pořadí nebo na zničené Activity.
ActivityResultLauncher podporuje všechny scénáře, které byly dříve zpracovávány přes onActivityResult: spouštění kamery, galerie, žádost o kontakty, oprávnění a vlastní Activity. Kromě toho je API rozšiřitelné: vývojář může vytvářet vlastní Contract pro specifické scénáře výměny dat mezi Activity.
startActivityForResult byl součástí Android SDK od verze API Level 1 (2008) a zůstal hlavním způsobem získávání výsledku z Activity po více než 12 let. Tato metoda však měla zásadní nedostatky, které Google odstranil v Activity Result API. Podívejme se na hlavní problémy a jak je nové API řeší.
Metoda startActivityForResult je spojena s Activity a Fragment prostřednictvím requestCode — libovolného celého čísla, které je předáno do onActivityResult. Vývojář ručně přiřazoval kód ke spuštěné operaci, což vedlo k chybám při opětovném použití kódu a dědičnosti. ActivityResultLauncher zcela odstraňuje requestCode: callback je navázán na konkrétní launcher ve fázi registrace a je volán pouze pro něj.
Při změnách konfigurace (otočení obrazovky, změna jazyka) byla Activity znovu vytvořena a onActivityResult se nemusel aktivovat — callback se ztratil. Activity Result API automaticky ukládá a obnovuje stav launcheru pomocí SavedStateRegistry, což garantuje přijetí výsledku i po znovuvytvoření Activity.
Staré API předávalo výsledek přes Intent s Bundle, kde klíče a datové typy nebyly kontrolovány kompilátorem. Activity Result API používá Contract — generické rozhraní, které definuje typ vstupních dat (I) a typ výsledku (O). Chyby typové neshody jsou detekovány ve fázi kompilace, nikoli za běhu.
| Vlastnost | startActivityForResult | ActivityResultLauncher |
|---|---|---|
| RequestCode | Vyžaduje ruční správu | Automatický, není vyžadován |
| Typová bezpečnost | Ne | Generic Contract |
| Uložení při otočení | Ztrácí se | SavedStateRegistry |
| Minimální API | API Level 1 | Activity 1.2.0 |
| Použití v Compose | Není podporováno | rememberLauncherForActivityResult |
Contract je rozhraní ActivityResultContract<I, O>, které definuje, jak spustit Activity a jak interpretovat výsledek. Google poskytuje sadu vestavěných kontraktů pro typické scénáře, pokrývající většinu potřeb vývojáře.
StartIntentSenderForResult je základní kontrakt pro spouštění IntentSender. Používá se v systémových scénářích, například při autentizaci přes Google Sign-In nebo při platbě přes Google Pay. Vstupní parametr — PendingIntent, výstup — ActivityResult s kódem a Intent.
RequestMultiplePermissions je kontrakt pro žádost o více oprávnění současně v Android 6.0+. Vstupní parametr — pole String s názvy oprávnění, výstup — Map<String, Boolean> s výsledkem každé žádosti. Dříve to vyžadovalo ruční parsování v onRequestPermissionsResult s přiřazováním kódů žádostí.
TakePicture je kontrakt pro fotografování přes systémový fotoaparát. Vstup — Uri, kam uložit snímek, výstup — Boolean (úspěch). TakeVideo funguje podobně s videem. Tyto kontrakty nahrazují zastaralý MediaStore.ACTION_IMAGE_CAPTURE s nestabilním chováním na různých zařízeních.
GetContent je kontrakt pro výběr obsahu přes systémový výběr. Vstup — MIME typ (např. image/*), výstup — Uri vybraného souboru. OpenDocument se liší podporou vícenásobného výběru a filtrováním podle typů dokumentů. Oba kontrakty fungují přes SAF (Storage Access Framework).
CreateDocument je kontrakt pro vytvoření nového dokumentu přes systémové dialogové okno. Uživatel vybere název a složku, systém vrátí Uri pro zápis. OpenDocumentTree poskytuje přístup k celému adresáři — uživatel vybere složku a aplikace získá tree-uri pro čtení a zápis všech souborů uvnitř.
Základní vzor použití ActivityResultLauncher v klasickém Androidu se skládá ze dvou kroků: registrace pomocí registerForActivityResult ve fázi inicializace a volání launch jako odpověď na akci uživatele. Podívejme se na typický příklad výběru obrázku z galerie.
Zaregistrujte launcher v onCreate Activity — to garantuje, že callback bude připraven před jakýmkoli možným voláním. Nikdy neregistrujte launcher bezprostředně před spuštěním — to porušuje kontrakt API a může vést ke ztrátě výsledku při znovuvytvoření Activity.
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/*")
}
}
Ve Fragment se registrace provádí v onCreate, onAttach nebo inicializaci v onCreateView. FragmentActivity předává launcher přes rodičovskou Activity, takže výsledek je zpracováván uvnitř Fragment, nikoli v Activity. To zlepšuje zapouzdření ve srovnání s onActivityResult, kde byly všechny výsledky ze všech Fragment shromažďovány v jedné metodě Activity.
class ProfileFragment : Fragment() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) { updateProfilePhoto() }
}
fun takePhoto(photoUri: Uri) {
cameraLauncher.launch(photoUri)
}
}
Jetpack Compose poskytuje speciální funkcionalitu composable pro Activity Result API — rememberLauncherForActivityResult. Na rozdíl od klasického přístupu je v Compose launcher vytvořen jako objekt navázaný na životní cyklus composable přes remember. To umožňuje používat Activity Result API plně deklarativním stylem bez přímého přístupu k Activity nebo Fragment.
rememberLauncherForActivityResult přijímá Contract a callback, vrací ActivityResultLauncher. Launcher je zachován při rekompozici a automaticky čištěn při opuštění kompozice. Volání launch nastává jako reakce na událost — například stisknutí tlačítka nebo změna stavu.
@Composable
fun PhotoPicker() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri -> handleImage(uri) }
Button(onClick = { launcher.launch("image/*") }) {
Text("Vybrat fotografii")
}
}
Žádost o oprávnění v Compose se také provádí pomocí rememberLauncherForActivityResult s kontraktem RequestPermission nebo RequestMultiplePermissions. Google doporučuje používat accompanist-permissions, ale pod kapotou také používá Activity Result API. Pro kontrolu stavu oprávnění je vhodné ukládat stav do remember nebo ViewModel.
Activity Result API odstranilo mnoho problémů starého přístupu, ale jeho nesprávné použití může vést k novým typům chyb. Podívejme se na nejčastější problémy a způsoby, jak se jim vyhnout.
Registrace launcheru musí proběhnout při inicializaci komponenty — v onCreate Activity nebo inicializátoru Fragment. Pokud registrujete launcher uvnitř lambdy, callbacku nebo korutiny, při znovuvytvoření Activity může být registrace provedena znovu a starý launcher ztratí spojení s výsledkem.
Každý launcher získává jedinečný klíč pro ukládání stavu. Pokud zaregistrujete dva launchery se stejným Contract v jedné komponentě, SavedStateRegistry může přepsat stav jednoho druhým. Android Studio na to upozorňuje pomocí lint pravidla UnnecessaryRegisterForActivityResult, ale je lepší kontrolovat jedinečnost ručně.
Uživatel může zrušit akci — stisknout systémové tlačítko zpět, minimalizovat aplikaci nebo přepnout na jinou aplikaci. V tomto případě callback obdrží null nebo ActivityResult s RESULT_CANCELED. Vždy kontrolujte výsledek na null před použitím, abyste předešli NullPointerException.
Pokud vaše aplikace často spouští podobné scénáře — například výběr kontaktu s vrácením jména a telefonu — vytvořte si vlastní Contract. To zlepšuje čitelnost kódu a umožňuje centrálně měnit logiku spouštění a zpracování výsledku.
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) }
}
Často kladené otázky
Ne — ActivityResultLauncher vyžaduje kontext Activity nebo Fragment pro registraci. Používejte ViewModel pouze pro ukládání stavu a launcher vytvářejte v Activity nebo Fragment a výsledek předejte do ViewModel.
Activity Result API je dostupné od knihovny activity-ktx 1.2.0. Minimální SDK — API Level 14 (Android 4.0), ale většina kontraktů funguje pouze na API Level 19+.
Opakované volání launch před dokončením první operace bude ignorováno. Activity Result API nepodporuje paralelní spouštění — počkejte na callback z první operace před novým voláním.
Migrace se provádí nahrazením volání startActivityForResult za registerForActivityResult s odpovídajícím Contract. Odstraňte onActivityResult a zpracovávejte výsledek v callbacku launcheru. Google poskytuje migrační příručku v dokumentaci Android Developers.
Ano, mnoho knihoven podporuje integraci přes ActivityResultContracts. Například ML Kit Barcode Scanner používá StartIntentSenderForResult ke spuštění skeneru. Zkontrolujte dokumentaci konkrétní knihovny.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také