How to setup Deep Links for Android applications

Deep links are special URIs that take users directly to specific content within a mobile app, rather than just launching the app or opening a webpage in a browser.

These links enhance user experience by letting users navigate to precise content or a particular section of the app with just one click.

On Android, there are clear distinctions between different types of links: Deep Links, Web Links, and Android App Links. To implement deep link handling on Android, it's essential to understand how each type behaves. The following illustration shows the relationships between these types of links.

Relation between different links
Relation between different links

In the image, you can see that Web Links and Android App Links are special cases of deep links. Let's explore them a bit further.

Deep links can take various forms depending on the URI scheme and the type of content they point to within the app. Here is an overview of possible schemes available with deep links:

1️⃣ HTTP/HTTPS Scheme

These are URLs that can open web pages in a browser or specific content within the app if the app is set up to handle them. Here is an example:

https://shopapp.com/shop
https://shopapp.com/profile

2️⃣ Custom Scheme

These are custom URI schemes defined by the app. They don't open in a web browser and are designed to be handled only by the app. Here is an example:

shopapp://shop
shopapp://profile

Notice that URI starts with shopap, this is called custom scheme. If there is an app on your Android device configured to handle this custom scheme, Android will delegate handling of the link to the capable app.

In simple terms, deep links are URIs that navigate users directly to specific content within a mobile app and can use both HTTP/HTTPS and custom URI schemes.

Web links are deep links that use the HTTP/HTTPS schemes. Starting with Android 12, clicking a Web Link that is not an Android App Link always shows content in a web browser.

On devices running previous versions of Android, if the app or other apps installed on a user's device can also handle the Web Link, users might not go directly to the browser. Instead, they'll see a system dialog letting them choose which app to use to open the link.

Android App Links are a special type of URL designed for Android that looks similar to a regular web link (using HTTP/HTTPS). They allow users to be directed to specific content within an app, instead of a web page. Here are some examples:

https://www.shopapp.com/product/67890
https://www.shopapp.com/profile/12345

When the user clicks on an Android App Link, the app opens immediately if it's installed. The Android system will not show any modals to the user; it just works. If the user doesn't want your app to be the default handler, they can override this behavior in the app's settings.

App Links are required to support deep linking behavior using HTTP/HTTPS scheme on Android 12 and onwards, ensuring a seamless user experience.

Implicit Intent

Before configuring deep links, it's important to understand how the Android system handles such links. This is done using something called an Intent.

In the world of Android apps, intents act as messengers. These messages coordinate actions between different parts of an app, or even between different apps entirely. There are two main types of intents: explicit intents and implicit intents. Today, we'll focus on implicit intents because they play a key role in deep linking and how the Android system directs users to specific app content.

Implicit intents specify a general action to be performed. They do not specify which application to use for performing the action. The system decides which installed application is best suited to handle the implicit intent. To achieve this, the system checks something called intent filters.

Deep links rely on intent filters specified in the app's AndroidManifest.xml to handle particular URL schemes or host/path combinations.

When a deep link is triggered (e.g., a user clicks a link), an implicit intent is created with the action Intent.ACTION_VIEW and a data URI specifying the URL to be handled.

Next, the Android system matches this intent against the intent filters declared by installed applications to find the appropriate activity to handle the link.

When setting up deep links, our goal is to ensure that the system has all the necessary data required to start our application when the designated deep link gets triggered.

Understanding intent filters

When you define an intent filter in your AndroidManifest.xml to handle deep links, the system uses the specified <data> attributes to match incoming intents to the correct activity. The <data> element can specify the scheme, host, port, path, pathPrefix, and pathPattern. If these attributes do not match the incoming URL, the intent filter will not match, and the activity will not be triggered (e.g., the application will not start).

Intent filters are crucial in handling deep links, allowing you to fine-tune how your application responds to specific links. We will discuss this in more detail in the next sections.

Deep links can seem complex at first, but the following flowchart will break it down into simpler steps. Let's see how deep links navigate users to specific app content.

Flowchart that shows how the Android handles deep links
Flowchart that shows how the Android handles deep links

The flowchart illustrates how the Android system resolves deep links, which can be in the form of a custom domain or an App Link.

The process begins with the user triggering the deep link. This can happen by pressing a button or clicking a link, for example in an email or messaging app. Upon this action, the Android system creates an implicit intent using the URL associated with the button or link.

The system checks the intent filters declared in AndroidManifest.xml for every installed app to find a matching filter that can handle the URL.

The system then checks if any installed apps have registered an intent filter that can handle the URL from the deep link and analyzes the deep link further to determine its specific type.

If the deep link uses a custom URI scheme but no matching app is found, the link isn't handled by the Android system. In all other cases, the link opens in a web browser.

If the system finds a matching intent filter, it takes further action based on the deep link type. For custom URI schemes, the link is directly opened within the corresponding app. However, for App Links, an additional step verifies domain ownership to ensure security. We'll explore this verification process in detail later. For now, let's focus on what happens after successful verification of an App Link.

Upon successful domain ownership verification, the link opens directly within the app. However, if verification fails, the link will be opened in a web browser for user safety.

I hope this gives you a clearer picture of how deep links are processed and handled by the system. If it's still not clear, consider reading this section once more.

Now, let's dive deeper and explore how to configure App Links to leverage this functionality within your own app!

As mentioned earlier, App Links are a special type of URL designed for Android that looks similar to a regular web link. Since App Links use the HTTP/HTTPS scheme, we need to configure them for designated web URLs.

Setting up App Links involves a few key steps to ensure that your application can handle URLs and open the app directly when those URLs are clicked.

Before we start, let's say we want to configure the app so that when the following link is clicked: https://shopapp.com/shop, it opens the ShopActivity inside the app itself.

There are a couple of important pieces of information that we can extract from the link:

  • Host: shopapp.com
  • Scheme: https
  • Pathname: /shop

We will later use this information to construct the required intent filter needed to open the app when the link is triggered. So, let's get started.

Update Manifest file

First, we need to declare intent filters in the AndroidManifest.xml for the activity that should handle our link.

<activity android:name=".ShopActivity">
<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" />
<!-- Define the scheme, host and path -->
<data
android:scheme="https"
android:host="shopapp.com"
android:pathPrefix="/shop"
/>
</intent-filter>
</activity>

So, what just happened? Let's break it down.

One very important attribute is android:autoVerify="true", and links will not work without it! According to Google, this attribute allows the app to designate itself as the default handler of a given type of link. So, when the user clicks on an Android App Link, your app opens immediately if it's installed.

The intent filter that we just created will only match https://shopapp.com/shop, but what if we want to open the app when https://shopapp.com link is clicked? Well, we can modify the intent filter and remove the android:pathPrefix="/shop" attribute, like this:

<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" />
<!-- Define the scheme and host -->
<data
android:scheme="https"
android:host="shopapp.com"
/>
</intent-filter>

This will match any URL with the scheme https and host shopapp.com, regardless of the path. So, URLs like https://shopapp.com, https://shopapp.com/shop, and https://shopapp.com/profile/1117 would all open the application.

You might wonder, when this is the case, why even use android:pathPrefix attribute? There are a couple of reason to consider, but most important is Selective Handling. This basically means that you can ensure that only specific paths within your domain are handled by your application.

If your website has different parts like /shop, /categories and /profile, and you only want the app to handle links under /shop, you would use android:pathPrefix="/shop" to ensure that the app only opens links that have /shop as a path.

Great, now we have intent filter that say to the system: “Hey, when you see this link https://shopapp.com/shop being clicked somewhere please open our app”.

But, this is still not enough. The system will not trust our app without proof that we own the link domain. Why, you might wonder?

Well, we could have added an intent filter for https://amazon.com/, but we obviously do not own this domain, and it would be really weird, and a major safety hazard, if the system would use our app to handle the link that points to the amazon.com domain.

So how can we prove to the system that we actually own the domain of the link? This is where the Digital Asset Links File comes into play. This file is used to prove the ownership of the domain (e.g., the association between your website and your app).

This file needs to be named assetlinks.json, and as the file extension implies, it needs to be in JSON format. This is how the file might look:

[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.shopapp.app",
"sha256_cert_fingerprints": [
"XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
]
}
}
]

You can see that the file is an array, and the configuration for our application is contained in an object. This means if you own multiple apps that can open the links from your website, you need to list the configuration for both (or more) apps in this file.

For Android apps, you will have to modify only the package_name and sha256_cert_fingerprints attributes. The package_name is pretty self-explanatory; it is the package name of your app.

In the sha256_cert_fingerprints attribute, you need to put the SHA-256 fingerprint of your app's signing certificate. There can be multiple fingerprints added (e.g., debug and production).

Download more icon variants from https://tabler-icons.io/i/alert-triangle

When adding certificate, it is important to use capital letters for HEX values! Not using capital letters might prevent the system from opening your app (errorCode: ERROR_CODE_MALFORMED_CONTENT).

If you do not know your SHA-256 fingerprint, you can use the following keytool command to get it:

keytool -list -v -keystore <path-to-keystore> -alias <key-alias> -storepass <keystore-password> -keypass <key-password>

Make sure to replace the following:

  • <path-to-keystore> - Path to the keystore file used to sign the app.
  • <key-alias> - Alias name given to the keystore when it was created.
  • <keystore-password> - Keystore password given to the keystore when it was created.
  • <key-password> - Key password given when the keystore was created.

It is important to add the correct SHA-256 fingerprint to the sha256_cert_fingerprints attribute or the link will not work!

If you are debugging the app using the default Android keystore, you can get the fingerprint for it using the following command and add it to the sha256_cert_fingerprints as well.

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

With this done, your assetlinks.json should be ready, and we can move to the next step.

We've created the assetlinks.json file, but it won't work its magic just sitting on your computer. Let's explore what the Android system needs from us to leverage this file for App Link verification.

When the Android system tries to verify the https://shopapp.com/shop link, it will make a GET request to a specific location on our website. In our example, the request URL will look like this:

https://shopapp.com/.well-known/assetlinks.json

Notice the .well-known/assetlinks.json path in the link. The Android system will try to get our Digital Asset Links File from that location. This means we need to upload/host our previously created assetlinks.json in that specific location.

Download more icon variants from https://tabler-icons.io/i/alert-triangle

The assetlinks.json file must be hosted in the .well-known directory at the root of the domain to work correctly.

That means, URL like https://shopapp.com/shop/.well-known/assetlinks.json will not work because /shop path is added.

Here's what the Android system expects for your assetlinks.json file to function correctly:

  • Directly Accessible: The file must be reachable without any redirects.
  • Open to Bots: The file needs to be accessible by automated programs (bots).
  • Correct Content Type: The file's content type should be identified as application/json.
  • Secure Connection: The file must be served over a secure HTTPS connection for added security.

In most cases, simply uploading your assetlinks.json file as a static file to https://<your_domain_name>/.well-known/assetlinks.json will be good enough to satisfy all the requirements.

Let's assume we have our Digital Assets Link File hosted on https://shopapp.com/shop/.well-known/assetlinks.json and ready to be used. How can we know that the configuration is correct and the Android system will be able to use it? Luckily, for this reason, Google provides a Digital Asset Links API to verify the accuracy.

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://<domain_name>&relation=delegate_permission/common.handle_all_urls

You can execute this request straight in you browser, make sure to replace <domain_name> with the correct domain name of your website.

If the API encounters any problems processing your assets file, it will return an error code along with additional details about the issue. This information can help you diagnose and fix problems within your asset file.

The best way to know if deep links are working is, obviously, to test them on the device. To test deep links, you can either use a real device or an Android emulator.

Before any testing can happen, let's follow some setup phases:

  • Make sure that AndroidManifest.xml is properly configured.
  • Use the correct signing certificate (keystore), the one you added to the assetlinks.json file.
  • Create new app build.
  • Install the app on device or emulator.

With the setup out of the way, we can start testing deep links. The first thing we can do is to artificially create test deep links using the adb command like this:

adb shell am start -W -a android.intent.action.VIEW \
-d "https://shopapp.com/shop"

This command simulates a deep link click on your connected device or emulator. It essentially tells the system to launch an activity capable of handling the provided URL, mimicking how the system would normally create an implicit intent for a deep link. Think of it as manually crafting an intent to test your app's deep link response. Remember to replace https://shopapp.com/shop with your actual deep link.

If our setup was correct, the System will open the link in our app rather than opening it in the browser.

For a more hands-on test, you can leverage the deep link itself! Send yourself an email or message containing the link, or generate a QR code from the link and scan it using your device's camera app. If the app is installed on your device, clicking the link or scanning the QR code should automatically launch the app and navigate to the intended content.

If you have some issues, make sure you followed the guide correctly and that nothing is skipped over.

Deep links with a custom scheme are another way to implement deep linking functionality. While deep links via the HTTP/HTTPS scheme are more commonly used, custom schemes can also come in handy in some cases.

So how to set it up? Luckily, it can be done in just one step compare to App Links that involved quite a lot things to go trough.

Update manifest file

As with App Links, we need to define an intent filter in the AndroidManifest.xml file to specify the custom URI scheme that the app will handle.

<activity android:name=".ShopActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Define custom scheme -->
<data android:scheme="shopapp" />
</intent-filter>
</activity>

The intent filter for a custom scheme deep link resembles the one for App Links, but with a key difference. The crucial attribute here is android:scheme, which specifies the custom scheme your app will respond to. In this example, the shopapp scheme is defined.

It goes without saying, but we also need to add code to the Activity to handle the deep link intent. However, as this is not part of our topic, we will skip it.

Believe it or not, with this setup, the configuration is actually completed. Our app should open when someone triggers a link like this one:

shopapp://shop

The Android system will create an intent, check intent filters, and open the app immediately without any validation or modals in between—it just works.

To test your custom scheme, similar to App Links, we can use the adb command.

adb shell am start -a android.intent.action.VIEW \
-d "shopapp://shop"

The command triggers an activity on the connected Android device or emulator to handle the specified URL, testing how the app responds to the deep link. If the app is installed and set up correctly, the link should open in the app.

Another way to test it us by creating a simple HTML file with a link that uses our custom scheme.

<!DOCTYPE html>
<html>
<head>
<title>Test Custom Scheme</title>
</head>
<body>
<a href="shopapp://shop">Open App</a>
</body>
</html>

You can use a simple static HTTP server to serve the file. Open the file on the device and click on the "Open App" button. When the link is triggered, the system should open the link in the app.

Wrapping up

Deep links are a powerful tool in today's mobile development landscape. Integrating deep links into your app can significantly improve user interaction by allowing users to access specific features or content directly from external sources.

We explored key deep link concepts and provided instructions for setting up deep links in Android applications. By understanding these concepts and implementing the discussed techniques, developers can leverage deep links to enhance user navigation, streamline user journeys, and ultimately create a more engaging app experience. Whether through custom URI schemes or verified web links (App Links), deep linking offers a robust solution for guiding users to specific content and improving the overall app experience.

© 2024 Ramo Mujagic. Thanks for visiting.