Tapping a link from a chat app is a bad way to test deep links, because the messenger often
rewrites or swallows the URL before your app sees it. The terminal removes all of that: you hand
the URL straight to Android's activity manager and watch what happens, with the same Intent your
app would receive in production. The command is adb shell am start.
The command Google gives you
The canonical form from the Android documentation is a single line:
adb shell am start -W -a android.intent.action.VIEW -d "example://gizmos" com.example.android
That fires a VIEW intent for example://gizmos, waits for the launch to
finish, and targets com.example.android. Swap in your own URI and package and you
have a working test. The ADB Command Generator builds the whole line from a
form if you would rather not type it.
am start, flag by flag
The flags that matter for deep link testing, from the activity manager reference:
-a <action>· the intent action, alwaysandroid.intent.action.VIEWfor deep links. Declare it once.-d · the data URI, which is your deep link itself. Once.-c <category>· an intent category, such asandroid.intent.category.BROWSABLE, to reproduce how a browser dispatches the link.-n <component>· a fully qualified component, likecom.example.app/.MainActivity, for an explicit intent targeting one activity.-e | --es <key> <value>· a string extra.--ez <key> <value>· a boolean extra.--eifor an integer extra.-f <flags>· intent flags, as supported bysetFlags().-W· wait for the launch to complete, soamreports the result instead of returning immediately.
Worked example: a custom scheme
For myapp://product/42, let the system resolve it the way a tap would:
adb shell am start -W \ -a android.intent.action.VIEW \ -d "myapp://product/42"
If one app handles the scheme, it launches. If the URI is unregistered, you get
Error: Activity not started, unable to resolve Intent, which is a useful answer:
the scheme is not registered the way you think. To rule out the resolver, target the activity
explicitly with -n:
adb shell am start -W \ -a android.intent.action.VIEW \ -d "myapp://product/42" \ -n com.example.app/.ProductActivity
Worked example: an HTTPS App Link
For a verified App Link, reproduce a browser-style dispatch by adding the BROWSABLE
category and letting the system pick the target:
adb shell am start -W \ -a android.intent.action.VIEW \ -c android.intent.category.BROWSABLE \ -d "https://example.com/product/42"
If your domain is verified, your app opens. If Chrome opens instead, the link is not verifying,
and the App Link troubleshooting guide is
the next stop. Forcing the package or -n launches your app regardless of
verification, handy for confirming the activity handles the URL while verification is still broken.
Passing extras and flags
Deep links sometimes arrive with extras. Attach them with the typed flags:
adb shell am start -W \ -a android.intent.action.VIEW \ -d "myapp://checkout" \ --es coupon "SUMMER" \ --ez guest true \ --ei step 2
Always wrap the URI in double quotes. A shell will interpret &, ?,
#, and spaces in an unquoted URL and mangle your query string before adb
sees it. If your link carries an encoded payload, confirm what reaches the device is what you
intended, because a missed layer of URL encoding is a classic "the link works but the parameters
are empty" bug.
Reading the output
With -W, am prints a short status block. Status: ok with a
TotalTime means the activity launched. Error: Activity not started, unable to
resolve Intent means nothing matched, which points at the scheme, host, or path.
Warning: Activity not started, its current task has been brought to the front means
the target was already on screen, which is fine. Pair this with adb logcat to see
what your app does with the Intent; the Logcat Filter Builder narrows the
output to your process.
When more than one device is connected
With an emulator and a phone both attached, adb refuses with
error: more than one device/emulator. List them and target one with -s:
adb devices adb -s emulator-5554 shell am start -W \ -a android.intent.action.VIEW \ -d "myapp://product/42"
The -s flag goes on adb itself, before shell. Worth baking
into any test script, since unqualified commands stop working the moment a second device appears.
Common questions
Why does am start say "unable to resolve Intent"?
No installed activity has an intent filter matching the action, scheme, host, and path you
passed. Either the app is not installed, the scheme is misspelled, or the path does not
match the pathPrefix or pathPattern in the manifest. Build the
filter with the Intent-Filter Generator and confirm the URI
falls inside the pattern it declares.
Can I test an iOS Universal Link with adb?
No. adb talks to Android only. The iOS equivalent is
xcrun simctl openurl booted "https://example.com/path" for the simulator, or
xcrun devicectl for a real device. The Deep Link Tester
covers both platforms with a QR code.
Do I have to pass the package name?
No. Leaving it off lets the system resolve the link the way a real tap would. Adding the
package or an explicit -n component forces your app to handle the URI even
when verification or disambiguation would send it elsewhere, useful for proving the
activity logic in isolation.
Keep reading
-
App Links vs Deep Links vs Universal Links
The pillar guide to how all three fit together.
-
When the link opens the browser
Fix verification before blaming the command.
Official docs
-
Android · adb and am
The activity manager command reference.
-
Android · Create deep links
Intent filters and the test command in context.