Using JavaScriptInterface to bridge WebView and Android
Android’s WebView can display a website inside an application, while a JavaScript interface lets that page request selected native Android actions. This is useful when an app combines HTML content with device features such as notifications, local storage, file selection, or SQLite data.
The bridge is a controlled communication layer rather than a general-purpose connection between JavaScript and Java. Android code exposes specific methods, JavaScript calls them through a named object, and the application decides which requests are safe to process.
For an Australian app, this pattern can support shoppers in Sydney, commuters in Melbourne, or users in regional Queensland who need a responsive offline-friendly interface. It also requires careful attention to mobile data use, privacy obligations under the Privacy Act 1988, and weaker connectivity outside major cities.
What the bridge does
A WebView renders HTML, CSS, and JavaScript within an Android activity. With addJavascriptInterface(), the activity adds a Java or Kotlin object to the page’s JavaScript environment. If the object is named AndroidBridge, JavaScript can call methods such as AndroidBridge.showMessage().
Only methods explicitly marked with @JavascriptInterface should be callable from JavaScript on modern Android versions. This annotation creates a clear public boundary. Methods that are private, internal, or unrelated to the web page should remain outside the exposed object.
The bridge is asynchronous in practical use. JavaScript can send a request to Android, but a native method should avoid blocking the WebView or performing lengthy database work on the main thread. Results can be returned through a callback, a later page evaluation, or a message-based design.
Build a small working example
The following Java activity enables JavaScript, loads a local page, and exposes a simple method. runOnUiThread() is used because Android UI operations must occur on the main thread.
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);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new AndroidBridge(), "AndroidBridge");
webView.loadUrl("file:///android_asset/index.html");
}
public class AndroidBridge {
@JavascriptInterface
public void showMessage(final String message) {
runOnUiThread(() ->
Toast.makeText(MainActivity.this, message,
Toast.LENGTH_SHORT).show()
);
}
}
}
The page in assets/index.html can call the method like this:
<button onclick="AndroidBridge.showMessage('Saved on this device')">
Save
</button>
For a production application, validate the incoming string, limit its length, and avoid passing sensitive information through logs. A button in an Australian retail app might send a product identifier to Android, but it should not send a customer’s complete payment details through an unrestricted JavaScript object.
Control data and thread boundaries
A bridge method should expose a small, predictable API. Prefer simple values such as strings, numbers, and booleans, then validate them in native code. JSON can represent structured data, but the Android side should parse it defensively and reject missing fields, unexpected types, and oversized payloads.
If a call reads SQLite data, accesses a file, or performs a network request, move that work to an executor or coroutine. Return the result only after the operation completes. Updating the page can then use webView.post() or evaluateJavascript() on the UI thread.
webView.post(() -> {
String safeJson = JSONObject.quote(result);
webView.evaluateJavascript(
"window.onNativeResult(" + safeJson + ")", null
);
});
Avoid building JavaScript by concatenating untrusted input. Quoting values correctly prevents a product name or user-entered note from becoming executable script. This matters when content is synchronised from a server or entered by users on shared devices.
Secure the WebView surface
The greatest risk is exposing a powerful native object to untrusted pages. A malicious page loaded into the same WebView could call every public annotated method. Restrict bridge use to trusted local assets or HTTPS origins that the application controls, and do not expose methods that launch arbitrary intents, read private files, or change account settings.
Disable unnecessary capabilities. Clear WebView data when appropriate, avoid mixed HTTP and HTTPS content, and use a restrictive WebViewClient to control navigation. For sensitive applications, verify the destination host before loading it and refuse unexpected redirects.
Native integration can involve very different security models across platforms; a Windows hook background illustrates why code that reaches beyond an application boundary deserves careful review. In Android, the WebView bridge should be treated as an application boundary even when its page looks like ordinary local HTML.
Handle navigation and lifecycle
A bridge belongs to the WebView that registered it. If the activity recreates after rotation, or a fragment replaces its view, register the interface again and release obsolete references. Holding an activity inside a long-lived bridge object can cause memory leaks.
Use a custom WebViewClient to keep approved links inside the app and send external links to the device browser when appropriate. Back navigation should also distinguish between WebView history and the activity’s normal back-stack behaviour.
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(
WebView view, WebResourceRequest request) {
Uri uri = request.getUrl();
return !"example.com".equals(uri.getHost());
}
});
The exact host check should support the application’s legitimate domains and account for subdomains carefully. A simple string comparison can be too permissive if an attacker registers a lookalike hostname.
Choose the right communication pattern
A direct interface is convenient for small, trusted interactions. For larger applications, Android’s Web Message APIs, URL interception, or a dedicated JavaScript-to-native event layer may provide clearer control. The best choice depends on origin validation, response complexity, and whether the page is local or remote.
| Pattern | Suitable use | Main strength | Main caution |
|---|---|---|---|
addJavascriptInterface() |
Small trusted local pages | Simple JavaScript calls | Dangerous with untrusted content |
evaluateJavascript() |
Sending a result to the page | Easy one-way response | Requires careful value escaping |
| Web Message APIs | Structured page communication | Better message separation | More setup and compatibility checks |
| URL interception | Basic commands or legacy pages | Works with simple web flows | Awkward for sensitive or large data |
| Native Android UI | Security-critical actions | Strong platform control | More development than HTML UI |
An app serving users across Australia may choose a local HTML interface for low-bandwidth screens, while keeping payments, identity checks, and permissions in native Android views. This division reduces the amount of private information handled by JavaScript and can make compliance reviews easier.
Test for Australian app conditions
Test with slow or interrupted connections, particularly if users may travel through regional New South Wales, Western Australia, or the Northern Territory. A local page should still explain failures clearly when an API request times out. Avoid assuming that every user has continuous 5G access in Sydney or Melbourne.
Check accessibility, screen sizes, keyboard behaviour, and WebView updates on devices distributed through the Australian Google Play market. Test dates, currency formatting in Australian dollars, and local phone-number validation if the page collects customer information.
Security testing should include hostile page content, malformed JSON, oversized strings, repeated button taps, activity recreation, and links to unapproved domains. Review what the bridge can access, what it logs, and how long personal information remains in WebView storage before releasing the application.