Working with App Links
1. Understanding Deep Links vs App Links
| Feature | Deep Links | App Links |
|---|---|---|
| URL Scheme | myapp://screen1 |
https://www.example.com/screen1 |
| Domain Verification | Not required | Required for Android 6.0+ |
| User Experience | May prompt user to choose app | Directly opens the app if installed |
| Security | Less secure, no verification | More secure with domain ownership proof |
| Fallback Behavior | May not open app if not installed | Can fall back to web page if app not present |
Example: Deep Link Intent
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("myapp://screen1"))
startActivity(intent)
Example: App Link Intent
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com/screen1"))
startActivity(intent)
2. Configuring Intent Filters
| Filter Type | Configuration | Description |
|---|---|---|
| intent-filter | <intent-filter> |
Defines a set of actions, categories, and data that an activity can handle. |
| action | <action android:name="android.intent.action.VIEW" /> |
Specifies the action that the intent filter handles, such as viewing a web page. |
| category | <category android:name="android.intent.category.DEFAULT" /> |
Indicates that the component can be used as a default handler for an intent. |
| data | <data android:scheme="https" android:host="example.com" /> |
Specifies the type of data that the intent filter can handle, such as a URL scheme and host. |
Example: Intent Filter Configuration
<activity
android:name=".MainActivity"
android:exported="true">
<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>
</activity>
3. Creating Asset Links File
Asset Links files are used to verify that your app is authorized to handle specific URLs. They must be hosted at https://yourdomain.com/.well-known/assetlinks.json.
| Field | Description | Required |
|---|---|---|
| relation | Specifies the action the app can perform. Must include delegate_permission/common.handle_all_urls. |
Yes |
| target | Defines the app and its signature hash. | Yes |
Example: assetlinks.json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": ["AB:CD:E1:F2:G3:H4:I5:J6:K7:L8:M9:N0:O1:P2:Q3:R4:S5:T6:U7:V8:W9:X0:Y1:Z2"]
}
}
]
Note: The SHA-256 fingerprint can be obtained using the command
keytool -list -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android.
4. Hosting Digital Asset Links
| Property | Value | Description |
|---|---|---|
| assetlinks.json | /.well-known/assetlinks.json |
Location where the digital asset links file is hosted |
| formatVersion | 1 |
Version of the asset links format |
| packageName | com.example.app |
Package name of the Android app |
| sha256Fingerprints | [SHA256_FINGERPRINT] |
SHA-256 fingerprint of the app's signing certificate |
Example: assetlinks.json content
{
"formatVersion": "1",
"packageName": "com.example.app",
"sha256Fingerprints": [
"A1:B2:C3:D4:E5:F6:G7:H8:I9:J0:K1:L2:M3:N4:O5:P6:Q7:R8:S9:T0:U1:V2:W3:X4:Y5:Z6"
]
}
Note: The
assetlinks.json file must be hosted at the root of your domain under /.well-known/assetlinks.json.
Warning: Ensure the SHA-256 fingerprint matches exactly with the app's signing certificate. Any mismatch will cause App Links to fail.
5. Verifying App Links
Verifying App Links ensures that your app correctly handles deep links and maintains security by validating the association between your app and web content.
| Verification Method | Description | Notes |
|---|---|---|
| App Links Assistant | Tool in Android Studio to analyze and verify App Links configuration. | Available in Android Studio Arctic Fox and later versions. |
| Digital Asset Links | JSON file hosted on your website to declare app-to-web associations. | Must be accessible at https://yoursite.com/.well-known/assetlinks.json. |
| Intent Filters | Define in AndroidManifest.xml to handle deep links. |
Must include android:autoVerify="true" for automatic verification. |
Example: Intent Filter with Auto-Verification
<activity
android:name=".MainActivity"
android:autoVerify="true">
<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="yourdomain.com" />
</intent-filter>
</activity>
Note: The
android:autoVerify="true" attribute enables automatic verification of App Links by the system.
Warning: If your app's intent filters do not match the digital asset links file, the system will not verify the App Links and may open the URL in a browser instead.
6. Handling Incoming Links
| Property | Description | Example |
|---|---|---|
| intent.action.VIEW | Used to handle incoming links in Android apps. | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com")) |
| intent.data | Represents the URI of the incoming link. | val uri = intent.data |
| intent.getParcelableExtra() | Used to retrieve data passed through the intent. | val data = intent.getParcelableExtra<MyData>("data") |
Example: Handling App Link in Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = intent
if (Intent.ACTION_VIEW == intent.action) {
val uri = intent.data
uri?.let {
// Handle the incoming link
handleIncomingLink(it)
}
}
}
private fun handleIncomingLink(uri: Uri) {
// Process the link
val path = uri.path
val query = uri.query
}
}
Note: Ensure that your app's manifest includes the appropriate intent filters to handle incoming links.
7. Using Navigation Deep Links
| Property | Description | Example |
|---|---|---|
| deepLink | URI pattern to match for deep linking | android-app://com.example.app/feature?id=123 |
| navigationGraph | Resource ID of the navigation graph file | R.navigation.nav_graph |
| uri | Full URI to navigate to | https://example.com/deeplink |
Example: Define Deep Link in Navigation Graph
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph">
<deepLink
android:id="@+id/deep_link"
app:uri="https://example.com/deeplink" />
</navigation>
Example: Handle Deep Link in Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
val navGraph = navController.navInflater.inflate(R.navigation.nav_graph)
// Handle deep link
val deepLink = intent.data
if (deepLink != null) {
navController.setGraph(navGraph, deepLink)
}
}
}
8. Implementing Deferred Deep Links
Deferred deep links allow you to redirect users to a specific part of your app even if they don't have the app installed. When a user clicks on a link, they are first directed to the web page and then, if the app is installed, redirected to the appropriate screen within the app.
| Property | Description | Example |
|---|---|---|
| android:autoVerify | Enables automatic verification of app links. | <intent-filter android:autoVerify="true"> |
| android:webAppManifest | Points to the web app manifest file for deferred deep links. | <meta-data android:name="android.webkit.WebAppManifest" android:value="@xml/web_app_manifest"> |
Example: Setting up deferred deep links in AndroidManifest.xml
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter
android:autoVerify="true"
android:label="@string/app_name">
<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>
</activity>
Example: Handling deferred deep links in Kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = intent
if (Intent.ACTION_VIEW == intent.action) {
val uri = intent.data
// Handle the deep link here
handleDeepLink(uri)
}
}
private fun handleDeepLink(uri: Uri?) {
// Process the deep link and navigate to appropriate screen
uri?.let {
val path = it.path
// Navigate based on the path or query parameters
}
}
}
Note: Ensure that your app's domain is verified with Google to support deferred deep links effectively.
Warning: Make sure the
android:autoVerify is set to true for automatic verification of app links.
9. Testing App Links
Example: Testing App Links with Android Studio
class AppLinkTest {
@Test
fun testAppLinkHandling() {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://example.com/deep-link")
)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
intent.setPackage("com.example.app")
val resolvedActivity = context.packageManager.resolveActivity(
intent,
PackageManager.MATCH_DEFAULT_ONLY
)
assertNotNull(resolvedActivity)
assertEquals("com.example.app", resolvedActivity?.activityInfo?.packageName)
}
}
| Test Type | Description | Notes |
|---|---|---|
| Instrumentation Tests | Use Espresso to test deep link handling in the app. |
Requires AndroidJUnitRunner |
| Unit Tests | Use Robolectric to simulate app link handling without device. |
Fast, no device required |
Testing Workflow
- Create a test intent with the deep link URL
- Add
CATEGORY_BROWSABLEandCATEGORY_DEFAULT - Set the package name to your app
- Resolve activity using
PackageManager - Assert that the correct activity is resolved
Note: Ensure your app's manifest includes the correct intent filters for deep links to be properly handled.
10. Debugging App Link Verification
Example: Using adb to verify App Links
adb shell am start -W -a android.intent.action.VIEW -c android.intent.category.DEFAULT -d "https://example.com" com.example.app
| Command | Description | Notes |
|---|---|---|
| adb shell am start | Starts an intent from the command line | Useful for testing App Link behavior |
| -W | Wait for the activity to be launched | Helps in debugging timing issues |
| -a android.intent.action.VIEW | Specifies the intent action to view a URL | Essential for App Link testing |
| -d https://example.com | Data URI to test | Replace with actual URL to verify |
Note: Ensure your app's manifest includes the correct intent filters for App Links to be handled properly.
Warning: App Link verification can be affected by network conditions and app state. Always test in a clean environment.