Android Development
Web Browser
Mobile Applications
URL Handling
Programming Tips

How can I open a URL in Android's web browser from my application?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

To open a URL in Android's browser, create an Intent with ACTION_VIEW and a parsed Uri. Android resolves the intent to the default browser or presents a chooser if multiple browsers are installed. For a more integrated experience, Chrome Custom Tabs open web content inside your app without a full browser launch. Since Android 11 (API 30), package visibility restrictions affect how you query for browser apps, requiring a <queries> declaration in your manifest.

Basic Intent Approach

java
// Java
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com"));
startActivity(browserIntent);
kotlin
// Kotlin
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com"))
startActivity(browserIntent)

Android finds an activity that handles ACTION_VIEW with an https:// URI — typically the default browser. This works for http://, https://, file://, and other registered URI schemes.

Handling Missing Browser

If no browser is installed (rare but possible on embedded devices), startActivity throws ActivityNotFoundException:

kotlin
1fun openUrl(context: Context, url: String) {
2    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
3    try {
4        context.startActivity(intent)
5    } catch (e: ActivityNotFoundException) {
6        Toast.makeText(context, "No browser app found", Toast.LENGTH_SHORT).show()
7    }
8}

Or check before launching:

kotlin
1val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
2if (intent.resolveActivity(packageManager) != null) {
3    startActivity(intent)
4} else {
5    // No browser available
6}

Forcing a Browser Chooser

If you want the user to pick a browser instead of using the default:

kotlin
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com"))
val chooser = Intent.createChooser(intent, "Open with")
startActivity(chooser)

Chrome Custom Tabs (In-App Browser)

Custom Tabs open web content inside your app with a Chrome-powered view, providing faster loading and a customizable UI:

groovy
1// build.gradle
2dependencies {
3    implementation 'androidx.browser:browser:1.7.0'
4}
kotlin
1import androidx.browser.customtabs.CustomTabsIntent
2import android.net.Uri
3
4val customTabsIntent = CustomTabsIntent.Builder()
5    .setShowTitle(true)
6    .setUrlBarHidingEnabled(true)
7    .build()
8
9customTabsIntent.launchUrl(this, Uri.parse("https://www.example.com"))

Custom Tabs advantages:

  • Faster than launching a full browser (pre-warming available)
  • Shares cookies and sessions with Chrome
  • Customizable toolbar color and animations
  • Falls back to a regular browser if Chrome is not installed

Customizing Chrome Custom Tabs

kotlin
1val builder = CustomTabsIntent.Builder()
2
3// Customize toolbar color
4builder.setDefaultColorSchemeParams(
5    CustomTabColorSchemeParams.Builder()
6        .setToolbarColor(ContextCompat.getColor(this, R.color.primary))
7        .build()
8)
9
10// Add animations
11builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left)
12builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right)
13
14// Show title
15builder.setShowTitle(true)
16
17val customTabsIntent = builder.build()
18customTabsIntent.launchUrl(this, Uri.parse("https://www.example.com"))

WebView (Embedded Browser)

For full control over web content rendering within your app:

kotlin
1// In your layout XML
2// <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" />
3
4val webView = findViewById<WebView>(R.id.webView)
5webView.settings.javaScriptEnabled = true
6webView.webViewClient = WebViewClient()  // Stay in app instead of opening browser
7webView.loadUrl("https://www.example.com")

Use WebView when you need to intercept navigation, inject JavaScript, or display web content as part of your app UI. Use intents or Custom Tabs for simply viewing external links.

Android 11+ Package Visibility

Starting with Android 11 (API 30), you need to declare which packages you query for in your manifest:

xml
1<manifest>
2    <queries>
3        <intent>
4            <action android:name="android.intent.action.VIEW" />
5            <data android:scheme="https" />
6        </intent>
7    </queries>
8
9    <!-- Internet permission for WebView -->
10    <uses-permission android:name="android.permission.INTERNET" />
11</manifest>

Without the <queries> block, intent.resolveActivity() may return null even when browsers are installed.

Jetpack Compose

kotlin
1@Composable
2fun OpenUrlButton(url: String) {
3    val context = LocalContext.current
4    val uriHandler = LocalUriHandler.current
5
6    Button(onClick = { uriHandler.openUri(url) }) {
7        Text("Open URL")
8    }
9}

LocalUriHandler is the Compose-idiomatic way to open URLs. It uses ACTION_VIEW internally.

Common Pitfalls

  • Not catching ActivityNotFoundException: On some devices (especially Android TV or embedded systems), no browser may be installed. Always wrap startActivity in a try/catch or check with resolveActivity() first.
  • Missing https:// scheme: Passing "www.example.com" without https:// causes the intent to fail because Android cannot determine the intent type. Always include the full scheme in the URL.
  • Forgetting <queries> on Android 11+: Without the <queries> declaration in your manifest, resolveActivity() returns null for browser intents on API 30+, even though browsers are available.
  • Using WebView for external links: WebView is heavyweight, requires managing the back stack, JavaScript, and security. For simply opening a link, use an intent or Custom Tabs which handle all of this automatically.
  • Not adding internet permission for WebView: <uses-permission android:name="android.permission.INTERNET" /> is required for WebView to load any URL. The intent-based approaches do not need this permission since the browser app handles networking.

Summary

  • Use Intent(ACTION_VIEW, Uri.parse(url)) to open URLs in the default browser
  • Use Chrome Custom Tabs for an in-app browser experience with faster loading and customizable UI
  • Use WebView only when you need full control over the web content rendering
  • Always handle ActivityNotFoundException for devices without browsers
  • Declare <queries> in your manifest for Android 11+ package visibility
  • In Jetpack Compose, use LocalUriHandler.current.openUri(url)

Course illustration
Course illustration

All Rights Reserved.