Designing a Secure, Extensible JS Bridge for Android Hybrid Apps
This article explores the challenges of JavaScript‑Native communication in Android WebView, compares Android‑to‑JS and JS‑to‑Android invocation methods, and presents a custom, secure, and extensible JS bridge framework with protocol design, interceptors, annotations, and implementation examples for hybrid app development.
Introduction
In mobile development, the pace is accelerating and native apps become cumbersome due to long development cycles, slow user upgrades, and lengthy app store reviews. Hybrid apps offer faster iteration, multi‑platform consistency, and short development cycles, but they suffer from performance gaps and limited hardware access. Existing solutions such as React Native, Ionic, and VasSonic address some issues, yet our project still faces fragmented communication, chaotic calls, and poor security when using WebView‑based JS bridges.
Android WebView JS Interaction
There are two ways for Android to call JavaScript:
WebView.loadUrl()
WebView.evaluateJavascript()
Before API 19 only loadUrl() was available; evaluateJavascript() introduced in API 19 offers higher efficiency and a callback for receiving results.
webView.evaluateJavascript("fromAndroid()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//do something
}
});JS can call Android code via three methods:
WebView.addJavascriptInterface()
WebViewClient.shouldOverrideUrlLoading()
WebChromeClient.onJsAlert(), onJsConfirm(), onJsPrompt() addJavascriptInterface() is the official recommendation. It requires enabling JavaScript and annotating methods with @JavascriptInterface. Example:
public class AppJavaScriptProxy {
private Activity activity = null;
public AppJavaScriptProxy(Activity activity) {
this.activity = activity;
}
@JavascriptInterface
public void showMessage(String message) {
Toast toast = Toast.makeText(this.activity.getApplicationContext(),
message, Toast.LENGTH_SHORT);
toast.show();
}
} webView.addJavascriptInterface(new AppJavaScriptProxy(this), "androidAppProxy"); // JS code
if (typeof androidAppProxy !== "undefined") {
androidAppProxy.showMessage("Message from JavaScript");
} else {
alert("Running outside Android app");
}Key points for JS‑to‑Android calls:
Methods must be public.
From API 17 onward, methods must be annotated with @JavascriptInterface.
The call is not executed on the main thread. shouldOverrideUrlLoading() intercepts URL requests; returning true handles the request, false lets WebView handle it. WebChromeClient callbacks work similarly.
In summary, the two approaches can be grouped into JavascriptInterface and request interception , each with pros and cons.
JS Bridge Framework Design
To solve the problems of diverse communication methods, chaotic calls, and weak security, we redesign the JS bridge by extracting the process from WebView and using request interception. The design includes a simple protocol ( jsbridge://method?a=123&b=345&jsCall=jsMethod1) and a five‑role architecture:
JsBridge – manages the framework and exposes external interfaces.
JsCall – abstracts a single request with its context and callback.
IProcessor – abstracts protocol parsing and classification.
JsProvider – supplies JS‑exposed methods, similar to JavascriptInterface.
@JsMethod – annotation for JS methods, analogous to @JavascriptInterface.
Security, usability, portability, and extensibility are the main design goals.
Protocol example:
jsbridge://method1?a=123&b=345&jsCall=jsMethod1Interception can also handle file transfer via Base64 or stream responses.
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return new WebResourceResponse("image/jpeg", "UTF-8",
new FileInputStream(new File("xxxx")));
}
});Framework skeleton includes logging, thread switching, and unified exception handling.
public class JsProcessor implements IProcessor {
public static final int TYPE_PROCESSOR = 1;
@Override
public int getType() { return TYPE_PROCESSOR; }
@Override
public boolean isProtocol(String url) {
return !TextUtils.isEmpty(url) && url.startsWith("jsbridge");
}
@Override
public IJsCall parse(Context context, WebView webView, final String url, Object webViewContext) {
return new IJsCall<RequestBean, ResponseBean>() {
private String mMethodName;
@Override
public void callback(ResponseBean data, WebView webView) {
JSBridge.callback(data, webView);
}
@Override
public String url() { return null; }
@Override
public RequestBean parseData() {
if (TextUtils.isEmpty(url)) return null;
Uri uri = Uri.parse(url);
String methodName = uri.getPath().replace("/", "");
mMethodName = methodName;
return new RequestBean(url);
}
@Override
public String method() { return mMethodName; }
};
}
}JS‑exposed methods are annotated with @JsMethod, specifying processor type, name, and permission level.
public class JsProvider {
@JsMethod(processorType = JsProcessor.TYPE_PROCESSOR, name = "method1", permission = "high")
public void method1(IJsCall jsCall) {
// do anything
jsCall.callback("xxxx");
}
}The complete communication flow is illustrated with an iframe injection example and URL interception in shouldOverrideUrlLoading().
var iframe = document.createElement('iframe');
iframe.setAttribute('src', 'jsbridge://method1?a=123&b=345');
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null; @Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
boolean handle = super.shouldOverrideUrlLoading(webView, url);
if (!handle) {
handle = JSBridge.parse(activity, webView, url);
}
return handle;
}Conclusion
Hybrid apps are the future, and robust JS‑Native communication is essential. The presented JS bridge framework offers a secure, low‑coupling solution that isolates communication details, supports annotations for method metadata, and can be extended with white‑list checks and data encryption.
Suishouji Tech Team
Suishouji's official tech channel, sharing original technical articles, posting recruitment opportunities, and hosting events. Follow us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
