Users expect to tap a link and land straight in-app, not going to the browser. With the new Android 15 (API level 35) arrival, Android’s App Links system has gained a powerful upgrade: Dynamic App Links. You get granular link-routing, exclusions, query-parameter rules and server-side updates, without shipping a new app version.
What’s changed
Previously, you’d declare deep-linking rules in your app’s AndroidManifest.xml, and publish a static assetlinks.json that just verified your app ↔ domain relationship. With Dynamic App Links you can:
- Publish path, fragment and query-parameter matchers in
assetlinks.json - Specify exclusions so particular URLs won’t open your app, useful for legacy content or web-first flows.
- Change link-routing logic on the server without pushing a new APK. Devices running Android 15 with Google services will refresh your JSON file periodically and merge dynamic rules with the manifest.
How it works in practice
1. In your Android app’s manifest you declare a broad intent-filter with android:autoVerify="true" for your domain.
<activity android:name=".MyDeepLinkActivity"
android:exported="true">
<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="example.com"
android:pathPrefix="/" />
</intent-filter>
</activity>
This sets the broad hostname/scheme scope.
2. On your website you host https://example.com/.well-known/assetlinks.json containing your app-association and the optional dynamic_app_link_components. Example:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
]
},
"relation_extensions": {
"delegate_permission/common.handle_all_urls": {
"dynamic_app_link_components": [
{ "/": "/products/*" },
{ "/": "/shoes", "?" : { "in_app": "true" } },
{ "/": "*", "exclude": true }
]
}
}
}
]
In this setup:
- URLs under
/products/*open your app. - The specific path
/shoes?in_app=trueopens your app only when that query param is present. - Everything else (
"/": "*") is excluded.
3. When a user taps a matching link and your app is installed, the system checks your rules, verifies your app↔domain, and then routes into the app (assuming Android 15+ and Google services present). On older devices it falls back to your static manifest rules.
Why this matters
- Campaign flexibility: Launch a seasonal promotion (
/promo/summer-sale) and later remove the rule without republishing your app. - Partner/vanity URLs: Add or remove partner links dynamically (
/partner/influencer-name). - A/B testing: Enable only a subset of users via query-param gating (
?in_app=true). - Safety & routing control: Exclude unsupported content from launching your app inadvertently.
Best practices for Android engineers
- Intent-filter in the manifest: choose a broad scope (scheme + host) but avoid overly specific paths there. Let the server rules refine.
- Ensure your
assetlinks.jsonis served at/.well-known/assetlinks.jsonover HTTPS, with no redirects, andContent-Type: application/json. - Order of rules matters: Android evaluates the dynamic rules in declared order and stops at the first match (including exclusions).
- Test on actual Android 15 devices (or emulator) with Google services to ensure the dynamic rules are applied. On older devices you’ll still use your manifest rules.
- Whenever you remove or change rules, expect a propagation delay (device polling) before the new behavior takes effect.
- Logging and monitoring: treat changes to
assetlinks.jsonlike feature releases—any mistake (syntax error) can deactivate all your dynamic rules fallback to manifest only.
https://developer.android.com/training/app-links/about#dynamic-app-links