Deep Links in Android: Getting Started

In this tutorial you’ll learn how to use intent filters to create deep links in to your Android app. By Harun Wangereka.

4.8 (13) ·

Download materials
Save for later

Having URLs that redirect to specific parts of an app is useful in marketing and user engagement. Deep links let you redirect users to a specific destination inside your app which provides better user experience.

In this tutorial, you’ll build PromoApp, an app that lets users navigate to a specific page by clicking a link. During the process you’ll learn about:

  • Deep link basics.
  • Creating and testing deep links.
  • Getting data from deep link URLs.
  • Handling deep links when the user doesn’t have your app installed.

Getting Started

Download the materials using the Download Materials button at the top or bottom of this tutorial. Open Android Studio 4.1 or later, and import the starter project.

Now, build and run the project. You’ll see the following screen:

Product with no Offer

The screen shows a product with an image, name, brand and price. Since everybody loves promotions you’re going to use deep links to add functionality to apply discounts to the product.

First, take a moment to learn about deep links.

Understanding Deep Links

A deep link is a URL that navigates to a specific destination in your app. When you click a deep link, Android:

  • Opens the user’s preferred app that can handle the link, if it’s available.
  • If the preferred app isn’t available, it opens the only app that can handle the link.
  • If multiple apps can handle the link, it opens a dialog that lets the user select from one of the apps that can open the link.

Now you know what a deep link is. Next, you’ll learn about the different parts of a deep link.

Building Blocks of a Deep Link

Consider the link It has the following parts:

  • https: Identifies the protocol used to access the resource on the internet.
  • The host, which is the name or address of the web server being accessed.
  • /test: The path which specifies a particular page of content.
  • code: The query parameter you can extract from intents in your destination. abcd is the value of the parameter.

Now that you understand the parts of a deep link, it’s time to create one for your app.

Understanding Intent Filters

To create a deep link to your app’s content you first have to create an intent filter. An intent filter specifies the types of intents the activity would like to receive. You define it in the manifest file.

An intent filter has the following elements:

  • action: For Google Search to reach your intent, you have to specify an ACTION_VIEW action.
  • data: Used to define URI format that resolves to the activity. You can have one or more in a single intent filter. The data tags have a scheme, host, path and query parameters to identify the deep link.
  • category: You have to define some categories for your deep link to work. First, you specify the BROWSABLE category so your deep link can work in a browser. Without it, clicking the deep link on a browser won’t resolve to your app.

    Second, the DEFAULT category lets your app handle implicit intents. If it’s missing, the intent must specify the app component name for the activity to start.

With this understanding of intent filters, you’ll create your first deep link in the next section. :]

Creating a Deep Link

Open AndroidManifest.xml. Replace TODO Add Deep Link Intent Filter with:

<intent-filter android:label="@string/text_deep_link_title">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
    android:pathPrefix="/test" />

With this code, you define the action, category, data and pathPrefix elements for your intent filter. You also add a label to the intent filter which lets the Activity handle deep links with the URL

Build and run. You won’t see any changes in the app but your app can handle deep links now.

Product with no Offer

Next, you’ll learn how to test your deep links.

Testing Your Deep Links

You’ll use the Android Debug Bridge, or ADB, shell commands to test the deep link flow. That is, you’ll check if the link navigates to the correct section of your app.

Note: To learn more about Android Debug Bridge, check out the Android Debug Bridge (ADB): Beyond the Basics tutorial.

Open your terminal and paste in the following command:

adb shell am start -W -a android.intent.action.VIEW -d ""

This command starts the ADB shell with the VIEW action and specifies the deep link URL.

Run the command above. You’ll see the following dialog:

Deep Link Select Dialog

Once you select Claim Offer, the app opens the specific destination. It looks like this:

Product with no Offer

Great! You’ve made your first deep link. In the next section, you’ll see how you can get data from the incoming intent in your activity.

Getting Data From Incoming Intents

When you open the destination, you can now access data from the link because it’s available with the intent. You can get this data either in onCreate or onNewIntent.

First, navigate to PromoActivity.kt. Add the following method below onCreate:

private fun handleIntent(intent: Intent?) {
  val appLinkAction: String? = intent?.action
  val appLinkData: Uri? = intent?.data
  showDeepLinkOffer(appLinkAction, appLinkData)

In the code above, you get the appLinkAction and appLinkData from the intent. You then pass them to showDeepLinkOffer.

Now, you might see some warnings due to missing imports. At the top of the file, add:

import android.content.Intent

Now, all the import errors are gone. But you still have an error on showDeepLinkOffer. To resolve this, add the following method below handleIntent:

private fun showDeepLinkOffer(appLinkAction: String?, appLinkData: Uri?) {
  // 1
  if (Intent.ACTION_VIEW == appLinkAction && appLinkData != null) {
    // 2
    val promotionCode = appLinkData.getQueryParameter("code")
    if (promotionCode.isNullOrBlank().not()) {
      activityPromoBinding.discountGroup.visibility = View.VISIBLE
      activityPromoBinding.tvPromoCode.text = promotionCode
      // 3
      activityPromoBinding.btnClaimOffer.setOnClickListener {
        activityPromoBinding.tvOfferClaimed.visibility = View.VISIBLE
    } else {
      activityPromoBinding.discountGroup.visibility = View.GONE

Here’s a code breakdown:

  1. You check if the action from the intent is ACTION_VIEW and also if the intent has data.
  2. Then you check if the link has a query parameter code and set the visibility of the discount UI to visible. You also display your promotion code to a TextView.
  3. Then you show a TextView with an offer claimed message when you click btnClaimOffer.

You’re one step away from claiming the discount. :] To claim your discount, add the following code to the end of onCreate:


Here, you call handleIntent(intent) and pass the intent from onCreate.

Build and run. The UI is still the same. Now, run the ADB command as before and select the PromoApp option.

You’ll see the following screen:

Promo Code from Deep Link

Tap CLAIM OFFER and boom! You claimed your offer as shown in the screenshot below:

Product with Offer From Deep Link Applied

You learned how to get data from incoming intents. In the next section, you’ll see how you can support other scheme types in your deep link.

Adding Custom Schemes

There are two types of schemes: http and https. Other links, such as tutorial://android/deeplink, don’t conform to these two types of schemes.

Deep linking supports such URLs without you having to add any modifications. The data tag for such a URL is:

  android:pathPrefix="/deeplink" />

You use tutorial as the scheme instead of the normal http or https. Android will handle the deep link the same way it did the others.

Handling Multiple URIs in One Activity

Sometimes you need to handle two URIs. In those cases, you can either:

  • Add many data tags in one intent filter. This isn’t recommended since data elements are merged together to represent all variations of their combined attributes. This might lead to the intent filter handling more URIs which isn’t the intention.
  • Create separate intent filters when your intention is to declare unique URLs. This is the recommended way to handle many URIs.

At this point, you know how the deep link work. But it only works when you have your app installed on the device. Next, you’ll see how to handle deep links when the user doesn’t have your app installed.

Handling Deep Links When the User Doesn’t Have Your App Installed

Your main goal is to provide the best experience when users click links that navigate to your app. For cases when the user doesn’t have your app installed, you would want the link to redirect the user to Play Store. Then the user can download the app and proceed to the specified section in your app.

Fortunately, Firebase Dynamic links can help you achieve better user experience across different platforms.

Firebase Dynamic Links usually work across different platforms in a similar manner even if your app isn’t installed. Whether the user opens the link on a web browser, in Android or in iOS, the link redirects to the specific destination in your app.

Note: Firebase Dynamic Links work best if you already have an app on the Google Play Store. In this tutorial, you’ll learn how to create Firebase Dynamic Links but, since the sample app isn’t on Play Store, you won’t be able to test the full flow. However, nothing else changes when you upload the app to Google Play Store.

Before you can use Firebase Dynamic Links, you need to create a subdomain.

Creating a Subdomain for Your Dynamic Link

To use Firebase Dynamic Links, you have to have a project in Firebase Console and connect your project to Firebase. Learn how to set up Firebase for your app in the “Rev it up!” section of our Firebase Tutorial for Android.

Note: Make sure to fill in the appropriate names for this app and use the package name when registering the app in Firebase. Once the app is registered, overwrite the google-services.json file with the newly generated one from your Firebase account. Feel free to use the provided google-services.json file though if you do not want to create your own Firebase project.

After your project is set, navigate to the Dynamic Links section on the left panel and under the Engage category. Click it and you’ll see:

First Dynamic Link setup

Click Get Started. You’ll see the steps for adding your subdomain.

Setting subdomain for Dynamic Link

Here, you add your domain for the dynamic link. If you have your own domain, you can configure it on Firebase, too. For this tutorial, you’re going to use the free custom subdomain.

Click Continue to get to the final step.

Subdomain setup and verification complete

This confirms you’ve verified the subdomain and gotten approval. Click Finish to complete setting up your subdomain. You’ll see something like this:

New Dynamic Link setup

From the image, you can see you haven’t added a Dynamic Link. You’ll do that in the next few steps.

Creating a Firebase Dynamic Link

Click New Dynamic Link. You’ll see the steps for creating one below:

Setting up URL for Dynamic Link

In the first step, you’ll customize your dynamic link. You can make it short or add more context to it. For now, you’ll use the default one generated by Firebase. Click Next.

Then, you’ll add your deep link URL and name. In this case, since you already have one, you’ll use the one shown in the image below:

Deep Link for Dynamic Link

Click Next. Now, you’ll define the behavior you want for iOS apps:

Dynamic Link Behavior on iOS

Firebase provides two options:

  1. Opening the link in a web browser.
  2. Opening the link in an iOS app. This option requires your Firebase project to have an iOS app added.

Select Open deep link URL in a browser option. Click Next.

Now, you’ll define link behavior in Android as shown below:

Dynamic Link Behavior on Android

For Android, select Open the deep link in your Android app. Then select your app from the dropdown, in this case the

If you can’t find the app in the dropdown, you might not have Firebase configured for your app. As mentioned earlier, you can do that by following the “Rev it up!” section in Firebase Tutorial for Android: Getting Started.

You also need to define the behavior you want when the user doesn’t have your app installed. The dialog gives you the option to send the user to a Google Play page for your app or a Custom URL or Google Play Instant Experience. For now, choose Google Play page for your app. Click Next.

There’s an optional setting where you can define the minimum Android version for your deep link. You won’t set that in this tutorial.

The last step covers tracking and adding social tags. While optional, this step is useful for measuring your campaign’s success.

Setting up Dynamic Links Campaign

Click Create. Congratulations! You created your first Firebase Dynamic Link. Next, you’ll handle dynamic links in your app.

Handling Dynamic Links in Your App

Before your app can handle Firebase Dynamic Links, you have to add intent filters with the URL you defined in Firebase Console. Because you’ve already added this in your AndroidManifest.xml you can skip this step.

Now, navigate to buid.gradle(app) and add the following dependencies:

implementation ''
implementation ''

Here, you add dependencies for Firebase Dynamic Links and Analytics so your app can gather statistics. Click Sync Now and wait for the build to complete.

Next, go to PromoActivity.kt. Add the following code below showDeepLinkOffer:

private fun handleFirebaseDynamicLinks(intent: Intent) {
  // 1
    .addOnSuccessListener { dynamicLinkData ->
      // 2
      if (dynamicLinkData != null) {
    // 3
    .addOnFailureListener(this) { e ->
      Log.d("DynamicLinkError", e.localizedMessage)

In the code above:

  1. First, you retrieve the link in your app and add a success listener.
  2. Second, you check if your link has data. Then you get the link from the data.
  3. Third, you add a failure listener for cases when you can’t get the link.

Add these imports to your imports to resolve errors:

import android.util.Log

Notice you still have an error on showDynamicLinkOffer. Add the following code below handleFirebaseDynamicLinks:

private fun showDynamicLinkOffer(uri: Uri?) {
  val promotionCode = uri?.getQueryParameter("code")
  if (promotionCode.isNullOrBlank().not()) {
    activityPromoBinding.discountGroup.visibility = View.VISIBLE
    activityPromoBinding.tvPromoCode.text = promotionCode
    activityPromoBinding.btnClaimOffer.setOnClickListener {
      activityPromoBinding.tvOfferClaimed.visibility = View.VISIBLE
  } else {
    activityPromoBinding.discountGroup.visibility = View.GONE

In the method above, you show the offer again, but this time the link comes from a Firebase Dynamic Link. You also show the offer only when the link has the code query parameter.

Next, add a call to handleFirebaseDynamicLinks() in onCreate():


Here you call handleFirebaseDynamicLinks() and pass the intent from onCreate().

Add the following code below super.onCreate(savedInstanceState):


You get an instance of Firebase Analytics so Google Analytics can track analytics events and show them in the Firebase console.

Build and run.

Promo Code from Deep Link

Open the link you generated in the Firebase console on a browser on your Android device. For this tutorial, you can use this link:

Opening the link redirects you to the app. Tap CLAIM OFFER:

Product with Offer From Deep Link Applied

Great! You claimed your offer from a Firebase Dynamic Link. Next, you’ll see the analytics available on Firebase Console.

Viewing Analytics Data

You added the necessary logic to record analytic events. Now, go to the Firebase console and open the Dynamic Links section. You’ll see something like this:

Firebase Dynamic Links Analytics Data

As you can see, you have the data on the number of:

  • Clicks
  • First opens
  • Re-opens

You can expand the link and see more insights about how your Dynamic Link is fairing.

Invoking a Deep Link Multiple Times

If your deep link activity stays in the foreground, and you invoke the deep link again, multiple instances of the activity stay in the backstack. To prevent this, add the following attribute to your activity definition in AndroidManifest.xml:


This code ensures only one instance of the activity can exist at a single time. It also helps you avoid running into unexpected backstack issues.

In PromoActivity.kt, add the following code below showDynamicLinkOffer:

override fun onNewIntent(intent: Intent?) {
  intent?.let { newIntent ->

Here you call handleIntent() with the intent received in onNewIntent which is called when your activity is in the foreground and receives a new intent. Depending on which method you use, you can call handleFirebaseDynamicLinks() or handleIntent().

Build and run. Click the link from Firebase Dynamic Link. You’ll see:

Promo Code from Deep Link

Now when you invoke the deep link multiple times, you’ll have the correct flow without unexpected issues.

Where To Go From Here?

Download the final version of this project using the Download Materials button at the top or bottom of this tutorial.

Congratulations! You learned how to use deep links to improve your app’s user experience. You also learned how to take advantage of Firebase Dynamic Links to create deep links that work across all platforms.

To learn how to create deep links when using the Jetpack Navigation Component, check out Navigation Component for Android Part 2: Graphs and Deep Links.

I hope you enjoyed the tutorial. If you have any questions or comments, please join the forum discussion below.