Quick Overview
Error generating quick overview
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
JsBridge
Android WebView ä¸ JavaScript ååéä¿¡æ¡¥æ¥åºã
A bridge between Java and JavaScript for Android WebView, providing safe bidirectional communication.
Architecture

Demo

Installation
// settings.gradle or build.gradle (project level)
repositories {
maven { url "https://jitpack.io" }
}
// build.gradle (module level)
dependencies {
implementation 'com.github.happydog-intj:JsBridge:v2.1.0'
}
Quick Start (BridgeWebView)
Add BridgeWebView to your layout:
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Initialize in your Activity:
BridgeWebView webView = findViewById(R.id.webView);
webView.setGson(new Gson());
// Register @JavascriptInterface handler
webView.addJavascriptInterface(
new MainJavascriptInterface(
webView.getCallbacks(),
webView.getPersistentCallbacks(),
webView),
"WebViewJavascriptBridge");
webView.loadUrl("file:///android_asset/demo.html");
Java â JavaScript
Register a JS handler, then call it from Java:
// JavaScript: register a handler
WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
document.getElementById("show").innerHTML = "data from Java: = " + data;
responseCallback("Javascript Says Right back aka!");
});
// Java: call the JS handler
webView.callHandler("functionInJs", new Gson().toJson(user), new OnBridgeCallback() {
@Override
public void onCallBack(String data) {
Log.d(TAG, "response from JS: " + data);
}
});
JavaScript â Java
Register a @JavascriptInterface method, then call it from JS:
// Java: create a JavascriptInterface class
public class MainJavascriptInterface extends BridgeWebView.BaseJavascriptInterface {
private WebViewJavascriptBridge mWebView;
public MainJavascriptInterface(Map<String, OnBridgeCallback> callbacks,
Map<String, OnBridgeCallback> persistentCallbacks,
WebViewJavascriptBridge webView) {
super(callbacks, persistentCallbacks);
mWebView = webView;
}
@Override
public String send(String data) {
return "default response";
}
@JavascriptInterface
public void submitFromWeb(String data, String callbackId) {
Log.d("JSInterface", "data from web: " + data);
mWebView.responseFromWeb("response from Java", callbackId);
}
}
// JavaScript: call the Java handler
WebViewJavascriptBridge.callHandler(
'submitFromWeb',
{'param': 'value'},
function(responseData) {
document.getElementById("show").innerHTML = "response: " + responseData;
}
);
Persistent Callbacks
By default, callbacks are removed after first invocation. Use persistent callbacks for multi-response scenarios (real-time updates, event streams):
// Java: callback survives multiple invocations
webView.callHandlerPersistent("functionInJs", data, new OnBridgeCallback() {
@Override
public void onCallBack(String data) {
Log.d(TAG, "called again: " + data); // can be called multiple times
}
});
Domain Whitelist (Security)
Restrict which origins can call native methods through the bridge. When a whitelist is set, only pages from allowed hosts can invoke @JavascriptInterface methods:
// Only allow your own domains
webView.addAllowedHost("example.com");
webView.addAllowedHost("*.example.com"); // wildcard for subdomains
// Or set all at once
Set<String> hosts = new HashSet<>(Arrays.asList("app.com", "*.app.com"));
webView.setAllowedHosts(hosts);
// Or use BridgeConfig for full control
BridgeConfig config = new BridgeConfig();
config.addAllowedHost("example.com");
webView.setBridgeConfig(config);
When the whitelist is empty (default), all origins are allowed â fully backward compatible.
BridgeHelper (Custom WebView Integration)
If you need JsBridge on a custom WebView (not BridgeWebView), use BridgeHelper:
Step 1: Implement IWebView
public class CustomWebView extends WebView implements WebViewJavascriptBridge, IWebView {
private BridgeHelper bridgeHelper;
public CustomWebView(Context context) {
super(context);
init();
}
private void init() {
getSettings().setJavaScriptEnabled(true);
bridgeHelper = new BridgeHelper(this);
setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
bridgeHelper.onPageStarted(); // reset JS injection state
}
@Override
public void onPageFinished(WebView view, String url) {
bridgeHelper.onPageFinished(); // inject bridge JS + flush queue
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return bridgeHelper.shouldOverrideUrlLoading(url);
}
});
}
// Delegate bridge methods
@Override
public void sendToWeb(String data, OnBridgeCallback responseCallback) {
bridgeHelper.sendToWeb(data, responseCallback);
}
public void callHandler(String handlerName, String data, OnBridgeCallback callBack) {
bridgeHelper.callHandler(handlerName, data, callBack);
}
public void registerHandler(String handlerName, BridgeHandler handler) {
bridgeHelper.registerHandler(handlerName, handler);
}
// ... other WebViewJavascriptBridge methods
@Override
public WebView getWebView() { return this; }
}
Step 2: Use domain whitelist with BridgeHelper
CustomWebView webView = findViewById(R.id.webView);
webView.bridgeHelper.addAllowedHost("example.com");
JavaScript Setup
The bridge JS is injected automatically on page load. Wait for it:
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
}
// Usage
setupWebViewJavascriptBridge(function(bridge) {
bridge.registerHandler('JS Echo', function(data, responseCallback) {
console.log("JS Echo called with:", data);
responseCallback(data);
});
});
Or listen for the ready event:
if (window.WebViewJavascriptBridge) {
// bridge is ready
} else {
document.addEventListener('WebViewJavascriptBridgeReady', function() {
// bridge is now ready
}, false);
}
ä¸æææ¡£
å®è£
// settings.gradle æ build.gradle (项ç®çº§)
repositories {
maven { url "https://jitpack.io" }
}
// build.gradle (模å级)
dependencies {
implementation 'com.github.happydog-intj:JsBridge:v2.1.0'
}
å¿«éå¼å§ (BridgeWebView)
å¨å¸å±ä¸æ·»å BridgeWebViewï¼
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
å¨ Activity ä¸åå§åï¼
BridgeWebView webView = findViewById(R.id.webView);
webView.setGson(new Gson());
// 注å @JavascriptInterface
webView.addJavascriptInterface(
new MainJavascriptInterface(
webView.getCallbacks(),
webView.getPersistentCallbacks(),
webView),
"WebViewJavascriptBridge");
webView.loadUrl("file:///android_asset/demo.html");
Java è°ç¨ JavaScript
å å¨ JS 端注å handlerï¼ç¶å Java 端è°ç¨ï¼
// JS 端: 注å handler
WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
console.log("æ¶å° Java æ°æ®: " + data);
responseCallback("æ¥èª JS çååº");
});
// Java 端: è°ç¨ JS handler
webView.callHandler("functionInJs", "æ¥èªJavaçæ°æ®", new OnBridgeCallback() {
@Override
public void onCallBack(String data) {
Log.d(TAG, "JS ååº: " + data);
}
});
JavaScript è°ç¨ Java
å建 @JavascriptInterface ç±»ï¼JS 端å³å¯è°ç¨ï¼
// Java 端: èªå®ä¹ JavascriptInterface
public class MainJavascriptInterface extends BridgeWebView.BaseJavascriptInterface {
private WebViewJavascriptBridge mWebView;
public MainJavascriptInterface(Map<String, OnBridgeCallback> callbacks,
Map<String, OnBridgeCallback> persistentCallbacks,
WebViewJavascriptBridge webView) {
super(callbacks, persistentCallbacks);
mWebView = webView;
}
@Override
public String send(String data) {
return "é»è®¤ååº";
}
@JavascriptInterface
public void submitFromWeb(String data, String callbackId) {
Log.d("JSInterface", "æ¶å° Web æ°æ®: " + data);
mWebView.responseFromWeb("æ¥èª Java çååº", callbackId);
}
}
// JS 端: è°ç¨ Java handler
WebViewJavascriptBridge.callHandler(
'submitFromWeb',
{'param': 'value'},
function(responseData) {
console.log("æ¶å° Java ååº: " + responseData);
}
);
æä¹ ååè°
é»è®¤åè°å¨é¦æ¬¡è°ç¨åèªå¨å é¤ãä½¿ç¨æä¹ ååè°å®ç°å¤æ¬¡ååºï¼å¦å®æ¶æ´æ°ãäºä»¶æµï¼ï¼
// Java 端: åè°ä¸ä¼å¨é¦æ¬¡è°ç¨åå é¤
webView.callHandlerPersistent("functionInJs", data, new OnBridgeCallback() {
@Override
public void onCallBack(String data) {
Log.d(TAG, "忬¡æ¶å°: " + data); // å¯è¢«å¤æ¬¡è°ç¨
}
});
ååç½ååï¼å®å ¨ç¹æ§ï¼
éå¶åªäºååå¯ä»¥éè¿ bridge è°ç¨ native æ¹æ³ã设置ç½åååï¼åªæå
许çååæè½è°ç¨ @JavascriptInterfaceï¼
// åªå
许èªå·±çåå
webView.addAllowedHost("example.com");
webView.addAllowedHost("*.example.com"); // æ¯æéé
符å¹é
ååå
// æè
䏿¬¡æ§è®¾ç½®
Set<String> hosts = new HashSet<>(Arrays.asList("app.com", "*.app.com"));
webView.setAllowedHosts(hosts);
ç½åå为空æ¶ï¼é»è®¤ï¼ï¼ææåååå 许 ââ å®å ¨ååå ¼å®¹ã
ä½¿ç¨ BridgeHelper èªå®ä¹ WebView
å¦æä½ éè¦å¨èªå®ä¹ WebView ä¸ä½¿ç¨ JsBridgeï¼èéç´æ¥ä½¿ç¨ BridgeWebViewï¼ï¼å¯ä»¥ç¨ BridgeHelperï¼
public class CustomWebView extends WebView implements WebViewJavascriptBridge, IWebView {
private BridgeHelper bridgeHelper;
public CustomWebView(Context context) {
super(context);
init();
}
private void init() {
getSettings().setJavaScriptEnabled(true);
bridgeHelper = new BridgeHelper(this);
setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
bridgeHelper.onPageStarted(); // é置注å
¥ç¶æ
}
@Override
public void onPageFinished(WebView view, String url) {
bridgeHelper.onPageFinished(); // 注å
¥ bridge JS + flush æ¶æ¯éå
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return bridgeHelper.shouldOverrideUrlLoading(url);
}
});
}
// å§æ bridge æ¹æ³å° bridgeHelper
public void callHandler(String handlerName, String data, OnBridgeCallback cb) {
bridgeHelper.callHandler(handlerName, data, cb);
}
public void registerHandler(String handlerName, BridgeHandler handler) {
bridgeHelper.registerHandler(handlerName, handler);
}
@Override
public WebView getWebView() { return this; }
// ... å
¶ä» WebViewJavascriptBridge æ¥å£æ¹æ³
}
å ³é®è¦ç¹ï¼
onPageStarted()ä¸è°ç¨bridgeHelper.onPageStarted()éç½®æ³¨å ¥ç¶æonPageFinished()ä¸è°ç¨bridgeHelper.onPageFinished()æ³¨å ¥ JS å¹¶ flush éåshouldOverrideUrlLoading()ä¸è°ç¨bridgeHelper.shouldOverrideUrlLoading(url)æ¦æª bridge URL
JS 端设置
Bridge JS ä¼å¨é¡µé¢å è½½å®æåèªå¨æ³¨å ¥ã使ç¨ä»¥ä¸æ¹å¼çå¾ bridge 就绪ï¼
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
}
// 使ç¨
setupWebViewJavascriptBridge(function(bridge) {
bridge.registerHandler('myHandler', function(data, responseCallback) {
console.log("æ¶å°æ°æ®:", data);
responseCallback("å¤ç宿");
});
});
éä¿¡éé
JsBridge æ¯æ 4 ç§éä¿¡ééï¼åè§æ¶æå¾ï¼ï¼
| # | éé | æ¹å | 说æ |
|---|---|---|---|
| â | URL Scheme æ¦æª | JS â Java | JS éè¿ iframe 触å yy:// schemeï¼Java 端 shouldOverrideUrlLoading æ¦æª |
| â¡ | @JavascriptInterface | JS â Java | JS ç´æ¥è°ç¨ Java 注åç @JavascriptInterface æ¹æ³ï¼API 17+ï¼ |
| ⢠| evaluateJavascript() | Java â JS | Java è°ç¨ evaluateJavascript() æ§è¡ JS 代ç ï¼API 19+ï¼ |
| ⣠| loadUrl("javascript:") | Java â JS | ä½çæ¬å
¼å®¹æ¹æ¡ï¼éè¿ loadUrl æ§è¡ JS |
JS æ³¨å ¥çå½å¨æ
onPageStarted â ç¶æé置为 NOT_LOADEDï¼æ¶æ¯é忢å¤
â
onPageFinished â æ³¨å
¥ bridge JS (evaluateJavascript)
â ç¶æ: LOADING
注å
¥å®æåè° â ç¶æ: LOADEDï¼flush æ¶æ¯éå
å¨ JS æ³¨å ¥å®æååéçæ¶æ¯ä¼èªå¨æéï¼æ³¨å ¥å®æåç»ä¸æ´¾å ââ ä¸åä¸¢æ¶æ¯ã
Compatibility / å ¼å®¹æ§
Android 11+ (API 30+)
JsBridge v2.1.0 å·²éé Android 11+ï¼
setAllowFileAccessFromFileURLs(false)/setAllowUniversalAccessFromFileURLs(false)é»è®¤å ³éï¼ä¸ API 30+ è¡ä¸ºä¸è´ï¼- å¦æä½ ç页é¢éè¿
file://å è½½ä¸éè¦è·¨æä»¶è®¿é®ï¼è¯·å¨åå§ååæå¨å¼å¯ï¼
webView.getSettings().setAllowFileAccessFromFileURLs(true);
clearCache(true)åLOAD_NO_CACHEå·²ä»é»è®¤init()ç§»é¤ ââ ç¼åçç¥åºç±å®¿ä¸» App å³å®setDomStorageEnabled(true)é»è®¤å¼å¯
HarmonyOS / 鸿è
æ¬é¡¹ç®ä¸º Android å¹³å°åºãHarmonyOS çæ¬è¯·åè社åºç§»æ¤ï¼
- ohpm:
@alvin917/jsbridge
WebView åå»ºå´©æº (rk3568 çåµå ¥å¼è®¾å¤)
妿éå° WebViewFactory / InflateException é误ï¼é常æ¯è®¾å¤ç WebView æä¾ç¨åºæªæ£ç¡®å®è£
æçæ¬è¿ä½ï¼å¸¸è§äº Rockchip çåµå
¥å¼å¼åæ¿ï¼ãè¿ä¸æ¯ JsBridge çé®é¢ï¼è§£å³æ¹æ³ï¼
- å¨è®¾å¤ä¸å®è£ /æ´æ° Chrome æ Android System WebView
- 确认
adb shell dumpsys webviewupdateè¾åºæ£å¸¸ - åµå ¥å¼è®¾å¤éè¦ç³»ç»éæåé¢è£ WebView APK
v2.1.0 Changelog
Bug Fixes
- #175: ä¿®å¤ URL decode ç ´åé bridge URL æ¥è¯¢åæ°ï¼å¦æ¯ä»å® deep linkï¼
- #265: ä¿®å¤æ¶æ¯éåå¨éè¯¯æ¶æºè¢«æ¸ é¤
- #209: ä¿®å¤ init åä¸è½ç«å³è°ç¨ JS æ¹æ³
- #250: ä¿®å¤é¢ç¹åæ¶æ¯å¯¼è´ Throttling navigation æ¥é
- #170: ä¿®å¤åå§åæ¶æ¶æ¯å¶åæ§ä¸¢å¤±
- #271: ä¿®å¤ CustomWebView (BridgeHelper) çå½å¨æç®¡ç
New Features
- æä¹
ååè°:
callHandlerPersistent() - ååç½åå:
BridgeConfig+addAllowedHost() - ç»ä¸æ¶æ¯æ¨¡å:
Message.createRequest()/createResponse() - JS 注å
¥ç¶ææº:
NOT_LOADED â LOADING â LOADED - 使ç¨
evaluateJavascript()+ åè°ç¡®è®¤æ³¨å ¥å®æ
Security / Compatibility
- ç§»é¤
init()ä¸çclearCache(true)åLOAD_NO_CACHEï¼ä¸åå¼ºå¶æ¸ é¤åºç¨ç¼åï¼ - é»è®¤ç¦ç¨
setAllowFileAccessFromFileURLs/setAllowUniversalAccessFromFileURLsï¼Android 11+ å®å ¨å åºï¼ - é»è®¤å¯ç¨
setDomStorageEnabled(true)
Build
- #275: ä¿®å¤ duplicate classï¼ä» åå¸ release AAR
- çæ¬å·: 2.1.0
License
This project is licensed under the terms of the MIT license.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot