Developing mobile applications on Android often requires organizing transitions between screens, external resources, or even other applications. One of the key scenarios is link processing: be it opening a web page in a browser, navigating to a specific screen within an application, or integrating with Deep Link for marketing campaigns. Without the correct implementation of these mechanisms, the user experience suffers, and the application loses functionality.

In this article we will analyze 5 ways to organize clicks on links in Android Studio - from the simplest Intent for opening a URL in the browser to complex schemes with PendingIntent and dynamic links Firebase. We will pay special attention to typical errors (for example, android.content.ActivityNotFoundException), code optimization for performance, and the nuances of working with Android 12+, where security policies have become more stringent. If you are just starting to develop software Android or want to systematize your knowledge, this guide will help you avoid pitfalls.

The easiest way is to redirect the user to a web page through the device's standard browser. For this purpose it is used Intent.ACTION_VIEW with a URI parameter. This method is suitable for external links (for example, to a support site, terms of use or social networks).

Example code for a button in Activity:

val url = "https://example.com"

val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))

startActivity(intent)

It is important to consider two points:

  • ๐Ÿ”น Checking browser availability: before calling startActivity check whether there is an application on the device that can handle Intent, otherwise the application will crash with an error. Use intent.resolveActivity(packageManager) != null.
  • ๐Ÿ”น HTTPS vs HTTP: starting from Android 9 (API 28), insecure connections (HTTP) are blocked by default. If your link does not support HTTPS, add android:usesCleartextTraffic="true" to AndroidManifest.xml.
โš ๏ธ Attention: If your application targets Android 12 (API 31) and above, working with Intent to external resources may require a declaration <queries> in the manifest. For example, to open links in Chrome add:
<queries>

<intent>

<action android:name="android.intent.action.VIEW" />

<data android:scheme="https" />

</intent>

</queries>

๐Ÿ“Š Which method of opening links do you use more often?
Intent.ACTION_VIEW
Deep Link
Firebase Dynamic Links
WebView
Other

2. Switching between screens within the application

If you need to open another screen (for example, a user profile or product details) by clicking on a link in the text or a button, use explicit Intent (Explicit Intent). This method is suitable for navigation within one application.

Example of transition from MainActivity to DetailActivity:

val intent = Intent(this, DetailActivity::class.java)

intent.putExtra("KEY_ID", 123) // Data transfer

startActivity(intent)

To process links in the text (for example, in TextView) use android:text="User profile" and configure the handler:

textView.movementMethod = LinkMovementMethod.getInstance()

textView.setOnClickListener {

val uri = Uri.parse("detail://user/123")

if (uri.scheme == "detail" && uri.host == "user") {

val intent = Intent(this, ProfileActivity::class.java)

intent.putExtra("USER_ID", uri.lastPathSegment?.toInt())

startActivity(intent)

}

}

  • ๐Ÿ“Œ Data transfer: use putExtra to pass primitives or Parcelable for complex objects. Avoid transferring large data through Intent as this may lead to TransactionTooLargeException.
  • ๐Ÿ”„ Processing the result: if you need to get data back (for example, after editing a profile), use startActivityForResult (obsolete since API 30) or Activity Result API.

โ˜‘๏ธ Preparing for the transition between screens

Done: 0 / 4

Deep Link allows you to open specific screens of your application using universal links (for example, myapp://product/42 or https://example.com/product/42). This is critical for marketing campaigns, push notifications and integration with other services.

To configure Deep Link:

  1. Add intent-filter to AndroidManifest.xml for target Activity:
    <activity android:name=".DetailActivity">
    

    <intent-filter>

    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />

    <category android:name="android.intent.category.BROWSABLE" />

    <data

    android:host="example.com"

    android:pathPrefix="/product"

    android:scheme="https" />

    </intent-filter>

    </activity>

  2. Process incoming Intent in Activity:
    override fun onCreate(savedInstanceState: Bundle?) {
    

    super.onCreate(savedInstanceState)

    handleIntent(intent)

    }

    override fun onNewIntent(intent: Intent?) {

    super.onNewIntent(intent)

    handleIntent(intent)

    }

    private fun handleIntent(intent: Intent?) {

    val data = intent?.data

    if (data != null && data.pathSegments.size > 1) {

    val productId = data.lastPathSegment

    // Load data for productId

    }

    }

Deep Link Type Link Format Does the installed application require? Support in Android
Custom scheme myapp://product/42 Yes All versions
HTTP/HTTPS https://example.com/product/42 No (opens in the browser) All versions
Android App Links https://example.com/product/42 No (the system automatically redirects to application). data-i="115">. Without this, the system will not trust your links. Android 6.0+ (API 23+)
Firebase Dynamic Links https://myapp.page.link/product/42 No (works even if the application is not installed) All versions
โš ๏ธ Attention: Starting from Android 12 (API 31), to work with Android App Links requires signing with a digital certificate and confirmation of domain ownership via Digital Asset Links. Without this, the system will not trust your links.
What are Digital Asset Links?

This is a JSON file hosted on your domain (for example, https://example.com/.well-known/assetlinks.json) that confirms the connection between a website and an Android application. It must be generated via Android Studio (Tools โ†’ App Links Assistant) and uploaded to the server.

Firebase Dynamic Links is a solution from Googlethat allows you to create "smart" links that work in any scenario:

  • ๐Ÿ”— If the application is installed, it opens a specific screen.
  • ๐ŸŒ If the application is not installed, it redirects to Play Market or to a web page.
  • ๐Ÿ“ฑ Supports delayed deep linking (for example, after installing an application).

An example of creating a dynamic link:

val dynamicLink = Firebase.dynamicLinks.dynamicLink {

link = Uri.parse("https://example.com/product/42")

domainUriPrefix = "https://myapp.page.link"

androidParameters("com.example.app") {

minimumVersion = 123

}

socialMetaTagParameters {

title = "Check out this product!"

description = "50% off today only"

imageUrl = Uri.parse("https://example.com/image.png")

}

}

val dynamicLinkUri = dynamicLink.uri

// Send a dynamicLinkUri to the user (for example, in a push notification)

To process an incoming link in the application:

FirebaseDynamicLinks.getInstance()

.getDynamicLink(intent)

.addOnSuccessListener { pendingDynamicLinkData ->

val deepLink = pendingDynamicLinkData?.link

if (deepLink != null) {

// Process a deepLink (for example, retrieve productId)

}

}

๐Ÿ’ก

Use Firebase Console to create short links and track conversion statistics. This will help analyze the effectiveness of marketing campaigns.

If your application uses WebView to display content, you may want to intercept link clicks and process them within the application (for example, open some URLs in WebViewand others in the browser or deep links).

Example settings WebViewClient:

webView.webViewClient = object : WebViewClient() {

override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {

val url = request?.url.toString()

return when {

url.startsWith("https://example.com/") -> {

// Open in WebView

false

}

url.startsWith("myapp://") -> {

// Process as Deep Link

val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))

startActivity(intent)

true

}

else -> {

// Open in browser

val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))

startActivity(intent)

true

}

}

}

}

Important nuances:

  • ๐Ÿ›ก๏ธ Security: always check the URL before processing to avoid Open Redirect type vulnerabilities (for example, when an attacker replaces a link to a phishing site).
  • ๐Ÿ”„ Navigation back: if WebView supports history, implement processing of the "Back" button through webView.canGoBack().

6. Typical errors and their solutions

When working with link transitions, developers often encounter the following problems:

Error Cause Solution
ActivityNotFoundException There is no application on the device that can process Intent (for example, there is no browser). Check availability through resolveActivity() or suggest installing a browser.
Deep Link does not work Incorrectly configured intent-filter or not confirmed Digital Asset Links. Check the manifest and file assetlinks.json via Statement List Tester.
Links in TextView not clickable Not installed MovementMethod or incorrect HTML format. Use LinkMovementMethod and the correct syntax <a href="...">.
TransactionTooLargeException Too large data is transmitted through Intent. Use ViewModeldatabase or SharedPreferences for storing data.

Another common problem is incorrect processing of links in Android 11+. Starting API 30, access to files and packages of other applications is limited. If your link leads to a local file (for example, file:///storage/emulated/0/...), use FileProvider or MediaStore API.

๐Ÿ’ก

Always test link clicks on real devices running different versions of Android. Emulators may not show problems related to manufacturers (for example, Xiaomi or Huawei some Intents are blocked).

How to check if a device supports Deep Link?

Use PackageManager to check availability Intent:

val intent = Intent(Intent.ACTION_VIEW, Uri.parse("myapp://test"))

val activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)

val isSupported = activities.isNotEmpty()

If isSupported == false, prompt the user to update the application or install it.

Is it possible to open the link in a specific browser (for example, only in Chrome)?

Yes, but this is not recommended, as it violates the principles Android (the user must select the application by default). If you still need, specify an explicit package:

val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))

intent.setPackage("com.android.chrome") // Chrome package

startActivity(intent)

Please note that the browser may not be installed, so process ActivityNotFoundException.

How to make the link open in the application and not in the browser?

Use Android App Links (see section 3). To do this:

  1. Set up intent-filter with httpsscheme.
  2. Confirm domain rights via Digital Asset Links.
  3. Test via adb:
    adb shell am start -a android.intent.action.VIEW -d "https://example.com/product/42"

If everything configured correctly, the system will offer to open the link in your application.

Why do Firebase Dynamic Links not work on some devices?

Problems may be associated with:

  • ๐Ÿ”ฅ Lack of Google Play Services (relevant for devices in China or with custom firmware).
  • ๐Ÿ›ก๏ธ Blocking Firebase firewall or VPN.
  • ๐Ÿ“ฑ Manufacturer restrictions (for example, Huawei uses its own AppGallery instead Play Market).

Solution: add a fallback mechanism (for example, opening a web version of the link) and test on devices without Google Services.

How to transfer data between screens without using Intent?

If the data is too large for Intent, use:

  • ๐Ÿ—„๏ธ ViewModel + LiveData (for UI-related data).
  • ๐Ÿ“ Room Database or SharedPreferences (for permanent storage).
  • ๐Ÿ”— Singleton or Dependency Injection (for example, Dagger Hilt).

Example with ViewModel:

class SharedViewModel : ViewModel() {

private val _data = MutableLiveData<String>()

val data: LiveData<String> = _data

fun setData(value: String) {

_data.value = value

}

}

// In the first Activity:

viewModel.setData("Hello");

// In the second Activity:

viewModel.data.observe(this) { value ->

textView.text = value

}