Az ActivityResultLauncher az Android Activity Result API komponense, amely az androidx.activity könyvtár Activity 1.2.0 verziójában került bevezetésre. Felváltja az elavult startActivityForResult és onActivityResult metódusokat, amelyek az Android SDK létrehozása óta annak részét képezték. A Android Developers (2024) szerint az új API kiküszöböli az Activity-hez való szoros kapcsolódás és a típusbiztonság hiányának problémáit. Az ActivityResultLauncher előre regisztrálásra kerül, és Contract-ot használ a bemeneti és kimeneti adatok szigorú típusosításához.
Főbb pontok
ActivityResultLauncher egy osztály az androidx.activity.result csomagból, amely típusbiztos mechanizmust biztosít az Activity elindításához és az eredmény fogadásához. A Launcher a registerForActivityResult metóduson keresztül jön létre, amely két paramétert fogad: Contract (leírja a bemeneti és kimeneti típusokat) és ActivityResultCallback (az eredmény feldolgozója). Regisztráció után a launcher készen áll a launch metóduson keresztüli meghívásra.
A legfontosabb különbség a régi API-hoz képest a regisztráció és az indítás szétválasztása. A regisztráció az inicializálási szakaszban történik (Activity.onCreate vagy Fragment.onCreate), a callback egyszer kapcsolódik a launcher-hez, és garantáltan aktiválódik az eredmény visszaérkezésekor. Ez kiküszöböli azt a problémát, amikor az onActivityResult váratlan sorrendben vagy egy megsemmisített Activity-n aktiválódott.
Az ActivityResultLauncher támogatja az összes olyan forgatókönyvet, amelyeket korábban az onActivityResult-on keresztül kezeltek: kamera indítása, galéria, kapcsolatok kérése, engedélyek és egyéni Activity-k. Emellett az API bővíthető: a fejlesztő saját Contract-okat hozhat létre az Activity-k közötti adatcsere specifikus forgatókönyveihez.
startActivityForResult az Android SDK API Level 1 (2008) verziója óta annak része volt, és több mint 12 évig az Activity-től való eredmény lekérésének fő módja maradt. Azonban ennek a metódusnak alapvető hiányosságai voltak, amelyeket a Google az Activity Result API-ban kiküszöbölt. Tekintsük át a fő problémákat, és hogy az új API hogyan oldja meg azokat.
A startActivityForResult metódus requestCode-on keresztül kapcsolódik az Activity-hez és Fragment-hez — egy tetszőleges egész szám, amely az onActivityResult-ba kerül továbbításra. A fejlesztő manuálisan párosította a kódot az elindított művelettel, ami hibákhoz vezetett a kód újrafelhasználásakor és öröklődéskor. Az ActivityResultLauncher teljesen megszünteti a requestCode-ot: a callback a regisztrációs szakaszban egy adott launcher-hez kapcsolódik, és csak azért hívódik meg.
Konfigurációs változásoknál (képernyőforgatás, nyelvváltás) az Activity újra létrejött, és az onActivityResult nem aktiválódhatott — a callback elveszett. Az Activity Result API automatikusan elmenti és visszaállítja a launcher állapotát a SavedStateRegistry segítségével, ami garantálja az eredmény fogadását még az Activity újra létrehozása után is.
A régi API az eredményt Intent-en keresztül Bundle-ben továbbította, ahol a kulcsokat és adattípusokat a fordító nem ellenőrizte. Az Activity Result API Contract-ot használ — egy generikus interfészt, amely meghatározza a bemeneti adatok típusát (I) és az eredmény típusát (O). A típus-eltérésekből adódó hibák a fordítási szakaszban észlelhetők, nem futásidőben.
| Jellemző | startActivityForResult | ActivityResultLauncher |
|---|---|---|
| RequestCode | Kézi kezelést igényel | Automatikus, nem szükséges |
| Típusbiztonság | Nincs | Generic Contract |
| Megőrzés forgatáskor | Elvész | SavedStateRegistry |
| Minimális API | API Level 1 | Activity 1.2.0 |
| Használat Compose-ban | Nem támogatott | rememberLauncherForActivityResult |
Contract egy ActivityResultContract<I, O> interfész, amely meghatározza, hogyan kell elindítani az Activity-t és hogyan kell értelmezni az eredményt. A Google beépített szerződések készletét kínálja a tipikus forgatókönyvekhez, amelyek a fejlesztői igények nagy részét lefedik.
StartIntentSenderForResult az alapszerződés az IntentSender elindításához. Rendszerszintű forgatókönyvekben használatos, például Google Sign-In-en keresztüli hitelesítéskor vagy Google Pay-en keresztüli fizetéskor. Bemeneti paraméter — PendingIntent, kimenet — ActivityResult kóddal és Intent-tel.
RequestMultiplePermissions a szerződés több engedély egyidejű kéréséhez Android 6.0+-ban. Bemeneti paraméter — String tömb az engedélyek neveivel, kimenet — Map<String, Boolean> az egyes kérések eredményével. Korábban ehhez kézi elemzésre volt szükség az onRequestPermissionsResult-ban a kérési kódok párosításával.
TakePicture a szerződés fénykép készítéséhez a rendszerkamerán keresztül. Bemenet — Uri, ahová a fénykép mentésre kerül, kimenet — Boolean (siker). TakeVideo hasonlóan működik a videóval. Ezek a szerződések felváltják az elavult MediaStore.ACTION_IMAGE_CAPTURE-t, amely instabil viselkedést mutatott különböző eszközökön.
GetContent a szerződés tartalom kiválasztásához a rendszer választóján keresztül. Bemenet — MIME típus (pl. image/*), kimenet — a kiválasztott fájl Uri-ja. OpenDocument abban különbözik, hogy támogatja a többszörös kiválasztást és a dokumentumtípusok szerinti szűrést. Mindkét szerződés SAF-en (Storage Access Framework) keresztül működik.
CreateDocument a szerződés új dokumentum létrehozásához a rendszer párbeszédablakán keresztül. A felhasználó kiválasztja a nevet és a mappát, a rendszer visszaadja az Uri-t az íráshoz. OpenDocumentTree hozzáférést biztosít egy teljes könyvtárhoz — a felhasználó kiválaszt egy mappát, és az alkalmazás tree-uri-t kap az összes fájl olvasásához és írásához.
Az ActivityResultLauncher használatának alapmintája a klasszikus Androidban két lépésből áll: regisztráció a registerForActivityResult segítségével az inicializálási szakaszban, és a launch meghívása a felhasználói műveletre válaszul. Tekintsünk egy tipikus példát a kép kiválasztására a galériából.
Regisztrálja a launcher-t az Activity onCreate-jében — ez garantálja, hogy a callback készen álljon minden lehetséges hívás előtt. Soha ne regisztrálja a launcher-t közvetlenül az indítás előtt — ez megsérti az API szerződést, és az Activity újra létrehozásakor az eredmény elvesztéséhez vezethet.
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-ben a regisztráció az onCreate-ben, onAttach-ban vagy az onCreateView-ben történő inicializáláskor történik. A FragmentActivity a launcher-t a szülő Activity-n keresztül továbbítja, így az eredmény a Fragment-en belül kerül feldolgozásra, nem az Activity-ben. Ez javítja az enkapszulációt az onActivityResult-hoz képest, ahol az összes Fragment összes eredménye az Activity egyetlen metódusában gyűlt össze.
class ProfileFragment : Fragment() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) { updateProfilePhoto() }
}
fun takePhoto(photoUri: Uri) {
cameraLauncher.launch(photoUri)
}
}
Jetpack Compose speciális composable funkcionalitást biztosít az Activity Result API-hoz — rememberLauncherForActivityResult. A klasszikus megközelítéssel ellentétben Compose-ban a launcher a remember segítségével a composable életciklusához kapcsolt objektumként jön létre. Ez lehetővé teszi az Activity Result API teljesen deklaratív stílusban történő használatát az Activity vagy Fragment közvetlen elérése nélkül.
rememberLauncherForActivityResult fogadja a Contract-ot és a callback-et, és visszaadja az ActivityResultLauncher-t. A launcher megőrződik a recompozíció során, és automatikusan törlődik a kompozícióból való kilépéskor. A launch hívása egy eseményre válaszul történik — például gombnyomásra vagy állapotváltozásra.
@Composable
fun PhotoPicker() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri -> handleImage(uri) }
Button(onClick = { launcher.launch("image/*") }) {
Text("Fotó kiválasztása")
}
}
Engedélyek kérése Compose-ban szintén a rememberLauncherForActivityResult segítségével történik a RequestPermission vagy RequestMultiplePermissions szerződéssel. A Google az accompanist-permissions használatát javasolja, de a motorháztető alatt az is az Activity Result API-t használja. Az engedélyek állapotának ellenőrzéséhez kényelmes az állapotot remember-ben vagy ViewModel-ben tárolni.
Activity Result API kiküszöbölte a régi megközelítés számos problémáját, de helytelen használata új típusú hibákhoz vezethet. Tekintsük át a leggyakoribb problémákat és azok elkerülésének módjait.
A launcher regisztrációját a komponens inicializálásakor kell elvégezni — az Activity onCreate-jében vagy a Fragment inicializálójában. Ha a launcher-t lambda, callback vagy korutin belsejében regisztrálja, az Activity újra létrehozásakor a regisztráció megismétlődhet, és a régi launcher elveszíti a kapcsolatot az eredménnyel.
Minden launcher egyedi kulcsot kap az állapot mentéséhez. Ha két launcher-t regisztrál ugyanazzal a Contract-tal egy komponensben, a SavedStateRegistry felülírhatja az egyik állapotát a másikkal. Az Android Studio figyelmeztet erre az UnnecessaryRegisterForActivityResult lint szabályon keresztül, de jobb az egyediséget manuálisan ellenőrizni.
A felhasználó megszakíthatja a műveletet — megnyomhatja a rendszer vissza gombját, minimalizálhatja az alkalmazást, vagy átválthat egy másik alkalmazásra. Ebben az esetben a callback null értéket vagy ActivityResult-ot kap RESULT_CANCELED-del. Mindig ellenőrizze az eredményt null-ra a használat előtt a NullPointerException elkerülése érdekében.
Ha az alkalmazása gyakran indít hasonló forgatókönyveket — például név és telefonszám visszaadásával történő kapcsolatválasztást — hozzon létre saját Contract-ot. Ez javítja a kód olvashatóságát, és lehetővé teszi az indítási és eredményfeldolgozási logika központosított módosítását.
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) }
}
Gyakran Ismételt Kérdések
Nem — az ActivityResultLauncher Activity vagy Fragment kontextust igényel a regisztrációhoz. A ViewModel-t csak állapot tárolására használja, a launcher-t pedig Activity-ben vagy Fragment-ben hozza létre, és az eredményt adja át a ViewModel-nek.
Activity Result API az activity-ktx 1.2.0 könyvtártól kezdve érhető el. Minimális SDK — API Level 14 (Android 4.0), de a legtöbb szerződés csak API Level 19+-on működik.
A launch ismételt meghívása az első művelet befejezése előtt figyelmen kívül lesz hagyva. Az Activity Result API nem támogat párhuzamos indításokat — várja meg az első művelet callback-jét az új hívás előtt.
A migráció a startActivityForResult hívásának registerForActivityResult-ra cserélésével történik a megfelelő Contract-tal. Távolítsa el az onActivityResult-ot, és kezelje az eredményt a launcher callback-jében. A Google migrációs útmutatót biztosít az Android Developers dokumentációjában.
Igen, sok könyvtár támogatja az integrációt az ActivityResultContracts-en keresztül. Például az ML Kit Barcode Scanner a StartIntentSenderForResult-ot használja a szkanner indításához. Ellenőrizze az adott könyvtár dokumentációját.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is