Back to Course
Navigation

Intents & Intent Filters — Navigating Between Screens

Intents are the glue that connects different components in Android. They allow you to start activities, start services, broadcast events, and send data between components. In simple terms, an Intent is a message that tells Android what you want to do.

Production Reality: Intents are one of the most frequently used features in Android. Understanding the difference between explicit and implicit intents, and knowing how to pass data safely, is essential for building robust applications.

1. What is an Intent?

An Intent is an object that describes an operation to be performed. It can be used to:

  • Start an Activity — Navigate to a new screen
  • Start a Service — Perform background operations
  • Send a Broadcast — Notify other components about events
  • Pass Data — Send data between components
Intent TypeDescriptionUse Case
Explicit IntentSpecifies the exact component to be calledNavigating within your own app
Implicit IntentDescribes the action to be performedOpening a URL, sharing content, taking a photo

2. Explicit Intents — Navigating Within Your App

An Explicit Intent directly specifies the component (Activity, Service, or BroadcastReceiver) that should handle the intent. It's used when you know exactly which component you want to start.

Basic Explicit Intent

// In MainActivity.kt — Start SecondActivity
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)

Passing Data with Explicit Intents

// Sending data — MainActivity.kt
val intent = Intent(this, SecondActivity::class.java).apply {
    putExtra("USER_NAME", "John Doe")
    putExtra("USER_AGE", 25)
    putExtra("IS_ACTIVE", true)
}
startActivity(intent)

// Receiving data — SecondActivity.kt
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        val userName = intent.getStringExtra("USER_NAME") ?: "Unknown"
        val userAge = intent.getIntExtra("USER_AGE", 0)
        val isActive = intent.getBooleanExtra("IS_ACTIVE", false)

        Log.d("SecondActivity", "Name: $userName, Age: $userAge, Active: $isActive")
    }
}

Passing Serializable Data

// User data class — must implement Serializable
data class User(
    val name: String,
    val email: String,
    val age: Int
) : Serializable

// Sending Serializable object
val user = User("John Doe", "john@example.com", 25)
val intent = Intent(this, SecondActivity::class.java).apply {
    putExtra("USER_OBJECT", user)
}
startActivity(intent)

// Receiving Serializable object
val user = intent.getSerializableExtra("USER_OBJECT") as? User
Pro Tip: For modern Android development, use Parcelable instead of Serializable for better performance. Kotlin's @Parcelize annotation makes this easy.

Getting Results Back from an Activity

// Starting activity for result — MainActivity.kt
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, REQUEST_CODE)

// In SecondActivity.kt — Sending result back
val resultIntent = Intent().apply {
    putExtra("RESULT_DATA", "Data from SecondActivity")
}
setResult(Activity.RESULT_OK, resultIntent)
finish()

// Receiving result — MainActivity.kt
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val result = data?.getStringExtra("RESULT_DATA")
        Log.d("MainActivity", "Result: $result")
    }
}
Production Warning: startActivityForResult() is deprecated in favor of the Activity Result API (introduced in AndroidX). Use ActivityResultLauncher for new projects.

Modern Approach — Activity Result API

// Register the launcher — MainActivity.kt
private val resultLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data = result.data?.getStringExtra("RESULT_DATA")
        Log.d("MainActivity", "Result: $data")
    }
}

// Launch the activity
val intent = Intent(this, SecondActivity::class.java)
resultLauncher.launch(intent)

3. Implicit Intents — Interacting with Other Apps

An Implicit Intent doesn't specify a specific component. Instead, it declares an action to be performed, and the Android system finds the best component to handle it.

Opening a URL

val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://google.com"))
startActivity(intent)

Opening a Phone Dialer

val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:+919999999999"))
startActivity(intent)

Making a Phone Call (Requires Permission)

// Add permission in AndroidManifest.xml
// <uses-permission android:name="android.permission.CALL_PHONE" />

val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:+919999999999"))
startActivity(intent)

Opening a Map Location

val intent = Intent(
    Intent.ACTION_VIEW,
    Uri.parse("geo:28.6139,77.2090?q=New+Delhi")
)
startActivity(intent)

Sharing Content (Share Sheet)

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

Opening an Email Client

val intent = Intent(Intent.ACTION_SENDTO).apply {
    data = Uri.parse("mailto:")
    putExtra(Intent.EXTRA_EMAIL, arrayOf("recipient@example.com"))
    putExtra(Intent.EXTRA_SUBJECT, "App Feedback")
    putExtra(Intent.EXTRA_TEXT, "Hello, I would like to provide feedback...")
}
startActivity(intent)

Taking a Photo (Implicit Intent)

// Requires file provider setup for Android 7.0+
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(packageManager) != null) {
    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}

// Handle result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
        val imageBitmap = data?.extras?.get("data") as? Bitmap
        imageView.setImageBitmap(imageBitmap)
    }
}

4. Intent Filters — Declaring Capabilities

Intent Filters are declared in the AndroidManifest.xml file to specify what types of implicit intents an activity can handle. This is how your app can respond to system-wide actions like sharing.

Declaring an Intent Filter

<activity android:name=".MainActivity">
    <!-- This activity is the launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!-- This activity can handle URL links -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="example.com" />
    </intent-filter>

    <!-- This activity can receive shared text -->
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

Handling Implicit Intents in Your Activity

// MainActivity.kt — Handle incoming intent
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // Check if this activity was launched by an implicit intent
    intent?.let { incomingIntent ->
        when (incomingIntent.action) {
            Intent.ACTION_VIEW -> {
                val data = incomingIntent.data
                Log.d("MainActivity", "Data: $data")
                // Handle URL or data
            }
            Intent.ACTION_SEND -> {
                val sharedText = incomingIntent.getStringExtra(Intent.EXTRA_TEXT)
                Log.d("MainActivity", "Shared Text: $sharedText")
                // Handle shared content
            }
        }
    }
}

5. Common Intent Actions

ActionDescriptionExample
ACTION_VIEWView data (URL, URI)Intent(Intent.ACTION_VIEW, Uri.parse("https://...")))
ACTION_DIALOpen dialer with a phone numberIntent(Intent.ACTION_DIAL, Uri.parse("tel:123"))
ACTION_CALLMake a phone callIntent(Intent.ACTION_CALL, Uri.parse("tel:123"))
ACTION_SENDShare dataIntent(Intent.ACTION_SEND).putExtra(...)
ACTION_SENDTOSend to a specific recipientIntent(Intent.ACTION_SENDTO, Uri.parse("mailto:"))
ACTION_PICKPick data (e.g., contact, image)Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
ACTION_GET_CONTENTGet content (file picker)Intent(Intent.ACTION_GET_CONTENT).setType("image/*")
ACTION_WEB_SEARCHPerform a web searchIntent(Intent.ACTION_WEB_SEARCH).putExtra(SearchManager.QUERY, "query")
ACTION_SET_ALARMSet an alarmIntent(AlarmClock.ACTION_SET_ALARM)

6. Common Pitfalls & Solutions

Common Mistakes to Avoid:

Pitfall 1: Not checking if an implicit intent can be handled

Problem: If no app can handle your intent, your app will crash.

Solution: Always check with resolveActivity() before starting an implicit intent.

val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://google.com"))
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
} else {
    Toast.makeText(this, "No app can handle this action", Toast.LENGTH_SHORT).show()
}

Pitfall 2: Passing large data objects

Problem: Intents have a size limit (~1MB). Passing large data can crash your app.

Solution: Use shared ViewModel or singleton for large data, or pass only an ID and fetch the data in the destination activity.

Pitfall 3: Not using the correct data type for extras

Problem: Retrieving extras with the wrong type (e.g., getStringExtra on an int) returns null.

Solution: Use the correct getter method and provide a default value:

val name = intent.getStringExtra("NAME") ?: "Unknown"
val age = intent.getIntExtra("AGE", 0)

7. Production-Ready Checklist

  • ✅ Use explicit intents — For navigation within your own app.
  • ✅ Use implicit intents — For interacting with other apps (share, call, view).
  • ✅ Check intent resolve — Always check resolveActivity() before starting implicit intents.
  • ✅ Use Activity Result API — For modern activity result handling.
  • ✅ Use Parcelable — For passing custom objects (performance over Serializable).
  • ✅ Keep intent data small — Avoid passing large data objects.
  • ✅ Use default values — When retrieving extras, always provide fallback values.
  • ✅ Handle intent filters properly — Declare intents correctly in AndroidManifest.xml.
  • ✅ Test deep links — If using implicit intent filters, test with adb shell am start.
  • ✅ Use Constants — Define intent keys in a companion object for consistency.

8. Quick Reference — Intent Extras

Data TypePut MethodGet Method
StringputExtra(key, value)getStringExtra(key)
IntputExtra(key, value)getIntExtra(key, default)
BooleanputExtra(key, value)getBooleanExtra(key, default)
FloatputExtra(key, value)getFloatExtra(key, default)
DoubleputExtra(key, value)getDoubleExtra(key, default)
LongputExtra(key, value)getLongExtra(key, default)
SerializableputExtra(key, value)getSerializableExtra(key)
ParcelableputExtra(key, value)getParcelableExtra(key)
String ArrayputExtra(key, value)getStringArrayExtra(key)
Int ArrayputExtra(key, value)getIntArrayExtra(key)
Key Takeaway: Intents are the backbone of Android communication. Mastering explicit intents for internal navigation and implicit intents for external interactions is essential. Remember to always check if an intent can be resolved before starting it, and use the modern Activity Result API for better code organization.

Ready to master Android Development?

Build real-world Android apps with hands-on projects, mentor-led sessions, and placement support.

Explore Course