androidengineers.Book a session

Core Android Components Deep Dive

Intent System & Deep Links Architecture

article20 minMedium

The Intent system is Android's backbone for component communication. Deep links extend it to the web. Getting both right is essential for a polished, linkable app.

Explicit vs Implicit Intents

Explicit: You specify the exact component to start. Use for in-app navigation.

// Explicit — only starts MyActivity in com.example
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("id", 42)
startActivity(intent)

Implicit: You describe what you want to do. The system finds a matching app.

// Implicit — any app that handles ACTION_VIEW for web URLs
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
startActivity(intent)

// Safe: check if any app can handle it first
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
}

PendingIntent: Security in Android 12+

PendingIntent wraps an Intent to be executed by another process (notifications, AlarmManager). Android 12 made FLAG_IMMUTABLE or FLAG_MUTABLE mandatory:

// ✅ Android 12+ compliant
val pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

// Use FLAG_MUTABLE ONLY when the receiving component must fill in extras
// (e.g., Bubbles or MediaSession callbacks that need to modify the intent)

Deep Links: Custom Schemes vs App Links

Custom scheme (myapp://detail/42): Works immediately, no verification. But any app can claim myapp://, creating phishing risk.

<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="myapp" android:host="detail" />
</intent-filter>

Android App Links (https://example.com/detail/42): Verified ownership — no chooser dialog, opens your app directly. Requires a /.well-known/assetlinks.json file on your server.

Setting Up Android App Links

1. Declare in AndroidManifest:

<intent-filter android:autoVerify="true">
    <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="www.example.com" />
</intent-filter>

2. Host assetlinks.json at https://www.example.com/.well-known/assetlinks.json:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.myapp",
    "sha256_cert_fingerprints": ["AA:BB:CC:..."]
  }
}]

3. Get your fingerprint:

keytool -list -v -keystore release.keystore | grep SHA256

Handling Deep Links with Navigation Component

// nav_graph.xml
<fragment android:id="@+id/detailFragment" ...>
    <deepLink
        android:id="@+id/deepLink"
        app:uri="https://example.com/detail/{id}" />
</fragment>
// In NavHostFragment's host activity — Navigation handles the intent automatically
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val navController = findNavController(R.id.nav_host_fragment)
        // Navigation automatically handles deep link intents from getIntent()
    }

    // Handle new intents (when Activity is already running)
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        findNavController(R.id.nav_host_fragment).handleDeepLink(intent)
    }
}

Testing Deep Links

# Test custom scheme
adb shell am start -W -a android.intent.action.VIEW \
    -d "myapp://detail/42" com.example.myapp

# Test App Link
adb shell am start -W -a android.intent.action.VIEW \
    -d "https://www.example.com/detail/42" com.example.myapp

# Verify App Links setup
adb shell pm get-app-links com.example.myapp

Key Takeaways

ConceptRule
Explicit intentAlways prefer for in-app navigation; check resolveActivity for implicit
PendingIntentAlways set FLAG_IMMUTABLE unless the receiver must fill extras
Custom schemesSimple but insecure; any app can register the same scheme
App LinksHTTPS-based, verified ownership, no chooser dialog
assetlinks.jsonMust be served over HTTPS at /.well-known/ on your domain
Navigation ComponentHandles deep link intents automatically from intent-filter declarations

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Intent System & Deep Links Architecture | Android System Design | Android Engineers