Understanding Explicit and Implicit Intents in Android: Passing Data Between Activities

Ramadan Sayed
7 min readAug 24, 2024

--

In Android development, Intents are an essential component that facilitates communication between different components of an application. They allow you to perform various tasks such as starting an activity, sending data, launching services, and broadcasting messages. Intents are divided into two main categories: Explicit Intents and Implicit Intents. In this article, we’ll explore both types in detail with practical examples using Kotlin and Jetpack Compose.

1. Explicit Intents

Explicit Intents are used when you want to launch a specific activity, service, or broadcast receiver within your application. In this type of intent, you explicitly define the component (such as an activity, service, or broadcast receiver) that you want to start or send data to.

Passing Data with Explicit Intents

One of the most common uses of explicit intents is to pass data from one activity to another. Let’s create a simple example where MainActivity sends a message to SecondActivity.

MainActivity.kt

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp


class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var message by remember { mutableStateOf("") }

Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(
value = message,
onValueChange = { message = it },
label = { Text("Enter your message") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
val intent = Intent(this@MainActivity, SecondActivity::class.java)
intent.putExtra("message", message)
startActivity(intent)
}) {
Text("Send to SecondActivity")
}
}
}
}
}

SecondActivity.kt

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

class SecondActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val message = intent.getStringExtra("message") ?: "No message received"
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Message received: $message", style = MaterialTheme.typography.headlineMedium)
}
}
}
}

Getting a Result from Another Activity

Sometimes you need to get a result back from the activity you started. Here’s how you can do it:

MainActivity.kt (with result handling)


import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {

private val getResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val returnMessage =
result.data?.getStringExtra("returnMessage") ?: "No result received"
updateResultMessage(returnMessage)
}
}

private var updateResultMessage: (String) -> Unit = {}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var message by remember { mutableStateOf("") }
var resultMessage by remember { mutableStateOf("") }

updateResultMessage = { newMessage ->
resultMessage = newMessage
}

Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(
value = message,
onValueChange = { message = it },
label = { Text("Enter your message") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
val intent = Intent(this@MainActivity, SecondActivity::class.java)
intent.putExtra("message", message)
getResult.launch(intent)
}) {
Text("Send to SecondActivity and Get Result")
}
Spacer(modifier = Modifier.height(20.dp))
ResultMessageDisplay(resultMessage)
}
}
}
}

@Composable
fun ResultMessageDisplay(resultMessage: String) {
if (resultMessage.isNotEmpty()) {
Text(text = "Result: $resultMessage", style = MaterialTheme.typography.bodyLarge)
}
}

SecondActivity.kt (sending the result back)

import android.app.Activity
import android.content.Intent

class SecondActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val message = intent.getStringExtra("message") ?: "No message received"
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Message received: $message", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
val resultIntent = Intent().apply {
putExtra("returnMessage", "Received your message: $message")
}
setResult(Activity.RESULT_OK, resultIntent)
finish()
}) {
Text("Send Result Back")
}
}
}
}
}

In this example, MainActivity sends a message to SecondActivity and waits for a result. When the user presses the button in SecondActivity, it sends the result back to MainActivity.

Using Bundles to Pass Multiple Data Types

If you need to pass more complex data or multiple data types between activities, you can use a Bundle. Here’s an example:

MainActivity.kt

import android.content.Intent
import android.os.Bundle

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val bundle = Bundle().apply {
putString("name", "John Doe")
putInt("age", 28)
putBoolean("isStudent", true)
}
val intent = Intent(this@MainActivity, SecondActivity::class.java).apply {
putExtras(bundle)
}
startActivity(intent)
}
}
}

SecondActivity.kt

class SecondActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val bundle = intent.extras
val name = bundle?.getString("name")
val age = bundle?.getInt("age")
val isStudent = bundle?.getBoolean("isStudent")

Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Name: $name")
Text(text = "Age: $age")
Text(text = "Student: $isStudent")
}
}
}
}

Best Practices for Using Explicit Intents

  1. Avoid Tight Coupling: Explicit intents can create tight coupling between activities. Consider using a central navigation controller or modularizing your components to reduce dependencies.
  2. Use Constants for Keys: When passing data between activities, use constants for the keys to avoid typos and make your code more maintainable.
  3. Handle Null Values: Always handle potential null values when retrieving data from an intent to avoid NullPointerException.

2. Implicit Intents

Implicit Intents do not specify the component to start. Instead, they declare a general action to perform, and the Android system finds the most appropriate component to handle the intent. This type of intent is especially useful for actions that could be handled by multiple apps, like viewing a webpage, sending an email, or sharing content.

Common Use Cases for Implicit Intents

1. Opening a Web Page

If you want to open a web page directly from your application, you can use an implicit intent. This intent triggers the default browser to open the specified URL.

import android.content.Intent
import android.net.Uri

val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://www.medium.com")
}
startActivity(intent)

2. Sending an Email

To send an email using an implicit intent, you can set up the intent to open the user’s default email application with the recipient’s address, subject, and body pre-filled.

val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:example@example.com")
putExtra(Intent.EXTRA_SUBJECT, "Subject Here")
putExtra(Intent.EXTRA_TEXT, "Body of the email")
}
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}

3. Sharing Content

Sharing text or other types of data across applications is a common use case for implicit intents. You can prompt the user to choose an app to share the content with.

val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "Check out this amazing article!")
type = "text/plain"
}
startActivity(Intent.createChooser(shareIntent, "Share via"))

Advanced Use Cases for Implicit Intents

Implicit intents can be used for more complex actions, like dialing a phone number, viewing a location on a map, or capturing an image. Here are some examples:

4. Dialing a Phone Number

You can prompt the user to dial a specific phone number using an implicit intent.

val intent = Intent(Intent.ACTION_DIAL).apply {
data = Uri.parse("tel:+1234567890")
}
startActivity(intent)

5. Viewing a Location on a Map

You can use an implicit intent to show a specific location on a map. This example uses Google Maps to display the location.

val gmmIntentUri = Uri.parse("geo:37.7749,-122.4194")
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri).apply {
setPackage("com.google.android.apps.maps")
}
if (mapIntent.resolveActivity(packageManager) != null) {
startActivity(mapIntent)
}

6. Capturing an Image with the Camera

To capture an image, you can use an implicit intent to open the camera app. The captured image can then be processed in your app.

val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}

Real-World Example: Integrating Multiple Implicit Intents in a Single UI

Let’s create an example app where the user can choose to perform different actions: open a web page, share a message, view a location, or dial a number. The UI will be built using Jetpack Compose.

MainActivity.kt

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var message by remember { mutableStateOf("Hello, World!") }

Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://www.medium.com")
}
startActivity(intent)
}) {
Text("Open Medium Website")
}

Spacer(modifier = Modifier.height(16.dp))

Button(onClick = {
val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("example@example.com")
putExtra(Intent.EXTRA_SUBJECT, "Greetings!")
putExtra(Intent.EXTRA_TEXT, message)
}
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
}) {
Text("Send an Email")
}

Spacer(modifier = Modifier.height(16.dp))

Button(onClick = {
val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "Check out this amazing article!")
type = "text/plain"
}
startActivity(Intent.createChooser(shareIntent, "Share via"))
}) {
Text("Share Text")
}

Spacer(modifier = Modifier.height(16.dp))

Button(onClick = {
val intent = Intent(Intent.ACTION_DIAL).apply {
data = Uri.parse("tel:+1234567890")
}
startActivity(intent)
}) {
Text("Dial a Number")
}
}
}
}
}

Best Practices for Using Implicit Intents

1. Check for Available Applications: Before launching an implicit intent, ensure that there is an application available to handle the intent by using resolveActivity.

if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}

2. Handle User Cancellation Gracefully: Always consider what happens if the user decides not to complete the action triggered by the intent. For example, if the user cancels sharing content, ensure your app doesn’t crash.

3. Use Intent Chooser: When using implicit intents that could be handled by multiple apps, provide the user with an intent chooser to pick their preferred app.

val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "Check out this amazing article!")
type = "text/plain"
}
startActivity(Intent.createChooser(shareIntent, "Share via"))

Conclusion

Intents are a fundamental part of Android development, enabling communication between different parts of your application and even between different apps. Whether you are passing data between activities using explicit intents or performing general actions like sharing content or opening a webpage with implicit intents, mastering the use of intents is crucial for building robust Android applications.

Connect with Me on LinkedIn

If you found this article helpful and want to stay updated with more insights and tips on Android development, Jetpack Compose, and other tech topics, feel free to connect with me on LinkedIn. I regularly publish articles, share my experiences, and engage with the developer community. Your feedback and interaction are always welcome!

Follow me on LinkedIn

--

--