
Android WebView Example Tutorial
Android WebView is a system component that allows your app to display web content directly within your application, essentially embedding a web browser into your Android app. It’s incredibly useful for hybrid app development, displaying remote content, integrating web-based features, or simply showing rich HTML content without launching the external browser. This tutorial will walk you through creating, configuring, and optimizing WebView implementations, covering everything from basic setup to advanced customization and performance optimization strategies.
How Android WebView Works
WebView is essentially a wrapper around the Chromium rendering engine that comes bundled with Android. Since Android 5.0 (API level 21), WebView has been updatable through Google Play, meaning the underlying browser engine stays current with security patches and feature updates regardless of the Android version.
The WebView component creates a sandboxed browser environment within your app’s process. It handles HTML parsing, CSS rendering, JavaScript execution, and network requests while providing APIs to interact between your native Android code and web content through JavaScript bridges.
Here’s what happens under the hood:
- Your app creates a WebView instance and loads it with content (URL, HTML string, or local file)
- The Chromium engine parses and renders the content
- JavaScript execution happens in a separate thread with V8 engine
- Network requests go through the system’s HTTP stack (can be intercepted)
- Rendered content is displayed in your app’s UI hierarchy
Basic WebView Implementation
Let’s start with a simple WebView setup. First, add the internet permission to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Create a basic layout with WebView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Now implement the basic WebView functionality in your Activity:
public class MainActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webview);
// Enable JavaScript (disabled by default)
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
// Set WebViewClient to handle page navigation
webView.setWebViewClient(new WebViewClient());
// Load a webpage
webView.loadUrl("https://www.example.com");
}
@Override
public void onBackPressed() {
// Handle back navigation in WebView
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
}
Advanced WebView Configuration
The real power of WebView comes from proper configuration. Here’s a more comprehensive setup with commonly needed settings:
private void configureWebView() {
WebSettings settings = webView.getSettings();
// JavaScript and DOM storage
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true);
// Caching strategy
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
settings.setAppCacheEnabled(true);
// Viewport and zoom
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setSupportZoom(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false); // Hide zoom buttons
// File access (be careful with security implications)
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
// Mixed content (HTTPS pages loading HTTP resources)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
// User agent customization
String userAgent = settings.getUserAgentString();
settings.setUserAgentString(userAgent + " MyApp/1.0");
}
Implementing WebView Clients
WebViewClient and WebChromeClient are essential for controlling WebView behavior. Here’s how to implement them effectively:
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString();
// Handle specific URLs differently
if (url.startsWith("mailto:")) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse(url));
startActivity(intent);
return true;
}
// Load internal URLs in WebView, external ones in browser
if (url.contains("yourdomain.com")) {
return false; // Load in WebView
} else {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true; // Don't load in WebView
}
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
// Show loading indicator
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// Hide loading indicator
progressBar.setVisibility(View.GONE);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request,
WebResourceError error) {
super.onReceivedError(view, request, error);
// Handle errors gracefully
if (request.isForMainFrame()) {
loadErrorPage();
}
}
});
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
progressBar.setProgress(newProgress);
}
@Override
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
// Handle JavaScript alerts
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", (dialog, which) -> result.confirm())
.show();
return true;
}
@Override
public void onPermissionRequest(PermissionRequest request) {
// Handle web permissions (camera, microphone, etc.)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
request.grant(request.getResources());
}
}
});
JavaScript Bridge Implementation
One of WebView’s most powerful features is the ability to create a bridge between JavaScript and native Android code. Here’s how to implement it:
public class WebAppInterface {
Context context;
WebAppInterface(Context c) {
context = c;
}
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(context, toast, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public String getDeviceInfo() {
return Build.MODEL + " (" + Build.VERSION.RELEASE + ")";
}
@JavascriptInterface
public void openNativeActivity(String activityName) {
Intent intent = new Intent(context, getActivityClass(activityName));
context.startActivity(intent);
}
}
// In your Activity
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
// JavaScript can now call:
// Android.showToast("Hello from web!");
// var deviceInfo = Android.getDeviceInfo();
To call JavaScript from Android:
// For API 19+
webView.evaluateJavascript("javascript:updateData('" + jsonData + "')",
new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
// Handle return value from JavaScript
}
});
// Legacy method (still works)
webView.loadUrl("javascript:updateData('" + jsonData + "')");
Loading Different Content Types
WebView can load content from various sources. Here are the common approaches:
// Load remote URL
webView.loadUrl("https://www.example.com");
// Load local HTML file from assets
webView.loadUrl("file:///android_asset/index.html");
// Load HTML string directly
String htmlData = "<html><body><h1>Hello World</h1></body></html>";
webView.loadData(htmlData, "text/html", "UTF-8");
// Load HTML with base URL (useful for relative links)
webView.loadDataWithBaseURL("file:///android_asset/", htmlData,
"text/html", "UTF-8", null);
// POST request with data
String postData = "param1=value1¶m2=value2";
webView.postUrl("https://api.example.com/submit", postData.getBytes());
Performance Optimization Strategies
WebView performance can significantly impact user experience. Here are key optimization techniques:
Optimization | Implementation | Impact |
---|---|---|
Hardware Acceleration | android:hardwareAccelerated=”true” | 30-50% rendering improvement |
Cache Strategy | LOAD_CACHE_ELSE_NETWORK | Reduces network requests by 60-80% |
Image Loading | setBlockNetworkImage(true) initially | Faster initial page load |
JavaScript Optimization | Lazy loading of JS interfaces | Reduces memory usage by 20-30% |
// Performance optimization setup
private void optimizeWebViewPerformance() {
WebSettings settings = webView.getSettings();
// Caching
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
settings.setAppCacheEnabled(true);
settings.setAppCachePath(getCacheDir().getAbsolutePath());
// Reduce memory usage
settings.setPluginState(WebSettings.PluginState.OFF);
settings.setGeolocationEnabled(false);
settings.setSaveFormData(false);
settings.setSavePassword(false);
// Improve rendering
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
}
// Load images after main content
settings.setBlockNetworkImage(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
settings.setBlockNetworkImage(false);
}
});
}
Security Best Practices
WebView security requires careful consideration, especially when loading external content or implementing JavaScript bridges:
private void secureWebView() {
WebSettings settings = webView.getSettings();
// Disable potentially dangerous features
settings.setAllowFileAccessFromFileURLs(false);
settings.setAllowUniversalAccessFromFileURLs(false);
settings.setJavaScriptCanOpenWindowsAutomatically(false);
// Secure JavaScript interface
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Only add interfaces on API 17+ due to security vulnerabilities
webView.addJavascriptInterface(new SecureWebInterface(), "Android");
}
// SSL error handling
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
// Don't ignore SSL errors in production
handler.cancel();
showSslErrorDialog(error);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
// Implement URL filtering/validation here
String url = request.getUrl().toString();
if (!isUrlAllowed(url)) {
return new WebResourceResponse("text/plain", "UTF-8",
new ByteArrayInputStream("Blocked".getBytes()));
}
return super.shouldInterceptRequest(view, request);
}
});
}
Common Issues and Troubleshooting
Here are the most frequent WebView problems and their solutions:
Issue 1: White screen or blank WebView
- Check internet permission in manifest
- Verify hardware acceleration is enabled
- Ensure JavaScript is enabled if required
- Check for SSL certificate issues
Issue 2: JavaScript not working
// Make sure JavaScript is enabled
webSettings.setJavaScriptEnabled(true);
// Check for console errors
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Log.d("WebView", consoleMessage.message() + " -- From line " +
consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
return super.onConsoleMessage(consoleMessage);
}
});
Issue 3: Memory leaks
@Override
protected void onDestroy() {
if (webView != null) {
webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
webView.clearCache(true);
webView.clearHistory();
webView.removeAllViews();
webView.destroyDrawingCache();
webView.destroy();
webView = null;
}
super.onDestroy();
}
WebView vs Alternatives Comparison
Solution | Pros | Cons | Best For |
---|---|---|---|
WebView | Integrated, customizable, offline capable | Performance overhead, security concerns | Hybrid apps, embedded web content |
Chrome Custom Tabs | Better performance, auto-updates, shared sessions | Less customization, requires Chrome | External web content, authentication |
External Browser | Best performance, always updated | Context switching, no integration | Simple web links, full websites |
Native Implementation | Best performance, full control | High development cost, maintenance | Core app features, complex UI |
Real-World Use Cases and Examples
E-commerce Product Gallery:
// Load product HTML with native checkout integration
webView.addJavascriptInterface(new CheckoutInterface(), "Checkout");
webView.loadUrl("file:///android_asset/product_gallery.html");
public class CheckoutInterface {
@JavascriptInterface
public void addToCart(String productId, int quantity) {
// Handle cart logic natively
CartManager.addItem(productId, quantity);
runOnUiThread(() -> updateCartBadge());
}
}
OAuth Authentication Flow:
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString();
if (url.startsWith("myapp://oauth-callback")) {
// Extract token from URL and complete authentication
String token = extractTokenFromUrl(url);
completeAuthentication(token);
return true;
}
return false;
}
});
Offline-First Content Viewer:
private void setupOfflineContent() {
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
String url = request.getUrl().toString();
// Check if resource exists in local cache
File cachedFile = getCachedFile(url);
if (cachedFile.exists()) {
try {
return new WebResourceResponse(
getMimeType(cachedFile),
"UTF-8",
new FileInputStream(cachedFile)
);
} catch (FileNotFoundException e) {
Log.e("WebView", "Cached file not found", e);
}
}
return super.shouldInterceptRequest(view, request);
}
});
}
For comprehensive WebView documentation, check the official Android WebView documentation and the WebView development guide.
WebView remains one of the most versatile components in Android development, bridging the gap between web and native experiences. With proper implementation, security considerations, and performance optimization, it can provide seamless integration of web content into your Android applications while maintaining native app performance characteristics.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.