Android Activity Result API — Complete Guide
In this tutorial, you'll learn about Android Activity Result API. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
The Problem
You use startActivityForResult() and onActivityResult() which are deprecated. Your result handling breaks when the Activity is recreated.
Wrong Approach ❌
// Deprecated approach
startActivityForResult(Intent(MediaStore.ACTION_IMAGE_CAPTURE), IMAGE_CAPTURE_CODE)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == IMAGE_CAPTURE_CODE && resultCode == RESULT_OK) {
val bitmap = data?.extras?.get("data") as? Bitmap
}
}
Output: Deprecated, fragment/activity coupling, lost results on rotation.
Right Approach ✅
class MainActivity : AppCompatActivity() {
// Take a picture
private val takePictureLauncher = registerForActivityResult(
ActivityResultContracts.TakePicture()
) { success ->
if (success) {
// Image saved to URI provided earlier
imageView.setImageURI(imageUri)
}
}
// Pick a document
private val pickDocumentLauncher = registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri: Uri? ->
uri?.let { loadDocument(it) }
}
// Custom contract
private val customLauncher = registerForActivityResult(
CustomContract()
) { result ->
handleResult(result)
}
private fun captureImage() {
imageUri = createTempImageUri()
takePictureLauncher.launch(imageUri)
}
private fun pickFile() {
pickDocumentLauncher.launch("image/*")
}
}
// Custom ActivityResultContract
class CustomContract : ActivityResultContract<InputType, ResultType>() {
override fun createIntent(context: Context, input: InputType): Intent {
return Intent(context, TargetActivity::class.java).putExtra("input", input)
}
override fun parseResult(resultCode: Int, intent: Intent?): ResultType {
return if (resultCode == Activity.RESULT_OK) {
intent?.getSerializableExtra("result") as? ResultType ?: defaultResult()
} else defaultResult()
}
}
Output: Clean, type-safe Activity result handling with lifecycle safety.
Prevention
- Replace all
startActivityForResultwithregisterForActivityResult. - Register launchers in
onCreate()or early initialization. - Use built-in contracts for common operations (take picture, pick contact, etc.).
- Create custom
ActivityResultContractfor complex interactions.
Common Mistakes with permission activity result
- Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists
These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro