Hybrid App (HTML5 + 原生) 中,JS 需呼叫原生功能 (相機、GPS、支付),原生需通知 JS 執行結果 (回呼、UI 更新)。
public class MainActivity extends DroidGap { // 或 AppCompatActivity + WebView
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadUrl("file:///android_asset/www/index.html");
WebSettings ws = webView.getSettings();
ws.setJavaScriptEnabled(true);
ws.setAllowFileAccess(true);
// 關鍵:註冊 Java 物件供 JS 存取
webView.addJavascriptInterface(new JsBridge(), "appBridge");
}
// 供 JS 呼叫的類別
public class JsBridge {
@JavascriptInterface // API 17+ 必須加註解
public void callNativeFunction(String param) {
Log.d("JS_BRIDGE", "Received: " + param);
// 執行原生邏輯...
}
@JavascriptInterface
public String getConfig() {
return "{\"version\":\"1.0\"}";
}
}
}
// 直接呼叫,參數自動轉型
window.appBridge.callNativeFunction("test data");
var config = window.appBridge.getConfig(); // 同步回傳 (僅限簡單型別)
// 構造 JS 代碼字串
String js = "javascript:onNativeCallback('" + resultUrl + "','" + resultId + "')";
// 載入執行
webView.loadUrl(js);
// 或使用 evaluateJavascript (API 19+,非同步、可取得回傳值)
webView.evaluateJavascript("onNativeCallback('" + resultUrl + "','" + resultId + "')",
new ValueCallback() {
@Override public void onReceiveValue(String value) {
Log.d("JS_RETURN", value);
}
});
function onNativeCallback(url, id) {
console.log("Native called back:", url, id);
updateUI(url, id);
return "OK"; // 可被 evaluateJavascript 接收
}
| 議題 | 說明 |
|---|---|
| 執行緒 | JS 呼叫 Java 在 WebView 核心執行緒 (非 UI 執行緒),若需操作 UI 必須 runOnUiThread。 |
| ProGuard | 保留 @JavascriptInterface 類別/方法,否則混淆後 JS 找不到。 |
| 安全性 | 僅暴露必要 API,避免注入風險;生產環境考慮 @Hide 或自定義權限檢查。 |
| 同源策略 | file:// 協議下 JS 可存取本地檔案,https:// 受同源限制。 |
| 回傳值 | loadUrl("javascript:...") 無法取得回傳值,需用 evaluateJavascript (API 19+)。 |
WKScriptMessageHandler + userContentController.addScriptMessageHandler。evaluateJavaScript:completionHandler:。addJavascriptInterface (Android) / WKScriptMessageHandler (iOS) 是橋樑核心。掌握執行緒模型、安全性、API 版本差異,即可構建穩健 Hybrid App。