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
| Concept | Rule |
|---|---|
| Explicit intent | Always prefer for in-app navigation; check resolveActivity for implicit |
PendingIntent | Always set FLAG_IMMUTABLE unless the receiver must fill extras |
| Custom schemes | Simple but insecure; any app can register the same scheme |
| App Links | HTTPS-based, verified ownership, no chooser dialog |
assetlinks.json | Must be served over HTTPS at /.well-known/ on your domain |
| Navigation Component | Handles deep link intents automatically from intent-filter declarations |