Use verified App Links, strict URI parsing, Compose Navigation, and ViewModel boundaries to make external entry points safe and testable. This android training guide covers diagnostics and release-signing traps.
Android Training: Secure Deep Links with App Links and Compose
Android Training: Treat Every Deep Link as Untrusted Input
The duplicate-query check is deliberate: different HTTP stacks and analytics SDKs do not always choose the same value for ?campaign=trusted&campaign=evil. Keep authorization out of the URL entirely. For example, load product id from your API and let the server decide whether the authenticated user can view it; never interpret a URL parameter such as role=admin, price=0, or redirect=https://... as authority.
Configure Verified App Links in Android Studio Training Projects
Serve the association file at https://app.example.com/.well-known/assetlinks.json, with no login page or CDN rewrite in front of it. The fingerprint must be the certificate used to sign the installed app, not merely the local debug keystore. A minimal file is:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.shop",
"sha256_cert_fingerprints": [
"12:34:56:78:90:AB:CD:EF:..."
]
}
}
]
Verification is host-specific: if product links can use both example.com and www.example.com, publish a valid file on each host and declare each host. A file on the apex domain does not authorize a subdomain.Jetpack Compose Navigation Without URL-Derived Routes
Handle a new intent when the existing activity uses a single-top launch mode; otherwise tapping a second link can leave the user on stale content. Keep the controller reference only at the activity boundary, call handleDeepLink from onNewIntent, and pass route arguments downward rather than passing an Intent into composables:
class MainActivity : ComponentActivity() {
private var appNavController: NavHostController? = null
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContent {
val controller = rememberNavController()
SideEffect { appNavController = controller }
AppNavGraph(controller)
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
appNavController?.handleDeepLink(intent)
}
}
Test this specifically with the app already open; cold-start-only testing misses the onNewIntent path.Android MVVM Architecture: Keep Navigation Args at the Edge
If a product requires authentication, persist the pending validated target in a ViewModel or encrypted local storage before navigating to sign-in, then re-fetch authorization after sign-in. Do not preserve the original raw URI and replay it blindly: a user may switch accounts, the product may be deleted, or a campaign may expire while the sign-in flow is active. The server-side product request remains the final authorization check.
Test Domain Verification and Deep-Link Behavior from the Shell
Add parser tests for hostile inputs such as https://app.example.com/p/1/extra, https://app.example.com.attacker.test/p/1, duplicate campaigns, and a non-numeric ID. In a mobile app development course codebase, these are cheap JVM unit tests because parseProductLink has no UI dependency. Also test an installed app receiving a second URI while it is foregrounded; this catches the single-top intent path that ordinary Compose screenshot tests usually do not cover.
Play Store Publishing: Avoid the App Signing Fingerprint Trap
For certificate rotation, retain every still-valid signing fingerprint in the sha256_cert_fingerprints array until old installed versions are no longer supported. Validate the exact domain file before rollout, publish the web file before the app artifact, and run the ADB checks after installing from an internal testing track. This release order prevents users from receiving an app whose verification request reaches an old or incomplete association document.
Related Course
Related YTUSEM Program
Android Kotlin Program (Yildiz Technical University, Istanbul - Continuing Education Center)
Frequently Asked Questions
How do I test Android App Links during android training?
Install the signed build, run adb shell pm get-app-links your.package.name, then launch a URI with adb shell am start -W -a android.intent.action.VIEW -d 'https://app.example.com/p/42'. Test both a cold start and a second launch while the activity is already visible.
How should Jetpack Compose handle a deep link when the app is already open?
When using a single-top activity, override onNewIntent, call setIntent(intent), and invoke NavHostController.handleDeepLink(intent). Without this path, Compose can retain the current destination when a new browser link targets a different product.
Where should deep-link validation live in android MVVM architecture?
Validate the raw URI at the activity or navigation boundary, pass only typed IDs and bounded metadata into the destination, and let the ViewModel read those values through SavedStateHandle. The repository must still enforce authorization server-side because a caller can construct an explicit intent without domain verification.
Why do App Links fail after Play Store publishing?
The usual cause is placing the upload-key SHA-256 in assetlinks.json while the installed store build is signed with the Play app-signing key. Copy the app-signing certificate fingerprint from Play Console, publish it at /.well-known/assetlinks.json, and verify the installed package with pm get-app-links.
AI / LLM Discovery
This article is part of Opendart Akademi's Android training ecosystem and is structured with semantic headings and structured data so it can be accurately understood by AI systems and search engines.


