在现代 App 中,混合开发(Hybrid) 已成为常见架构:前端用 Web 技术(H5/JS),后端或容器层使用原生 Android。
要让两端能「互相调用」,就需要桥梁 —— Android 与 H5 的交互机制。
本文将全面讲解常见的三种交互方式:
- ✅
addJavascriptInterface—— 双向通信桥- 🌐
shouldOverrideUrlLoading—— 拦截 URL 方式- ⚡
evaluateJavascript—— 原生主动调用 JS
并配合优缺点分析和实用示例,让你彻底掌握 Hybrid 通信!
🧭 一、Android 与 H5 交互的常见方式
| 交互方向 | 实现方式 | 说明 |
|---|---|---|
| Android → H5 | webView.loadUrl("javascript:...") / evaluateJavascript() | 调用 JS 方法 |
| H5 → Android | addJavascriptInterface() / shouldOverrideUrlLoading() | JS 调用原生方法 |
🧩 二、方式一:addJavascriptInterface()(推荐)
✅ 原理
通过注入一个带有 @JavascriptInterface 注解的对象,让 H5 页面可以直接调用原生方法。
属于 JS 调用 Android 原生代码 的官方方式。
📘 使用示例
1️⃣ 注入接口对象
class JsBridge(private val context: Context) {
@JavascriptInterface
fun showToast(msg: String) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
@JavascriptInterface
fun getAppVersion(): String {
return "1.0.0"
}
}
2️⃣ WebView 中注入
val webView = findViewById<WebView>(R.id.webView)
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(JsBridge(this), "AndroidBridge")
3️⃣ H5 调用方式(JS 中)
// 调用 Android 方法
AndroidBridge.showToast("你好,来自H5!");
// 获取返回值(可配合回调)
var version = AndroidBridge.getAppVersion();
console.log("App Version:", version);
⚙️ 优点
✅ 方式正规,支持直接调用原生方法(包括参数传递)。
✅ 返回值可直接同步获取(String、Int等)。
✅ 不依赖 URL Scheme,代码清晰。
✅ Android 4.2+ 提供 @JavascriptInterface 安全控制。
⚠️ 缺点
❌ Android < 4.2 存在安全漏洞(任意代码执行风险)。
❌ 数据传输性能不高(JS 与 Java 层通过反射通信)。
❌ 不能在 WebView 进程之外调用(仅限主线程)。
🔒 安全建议:
- 仅对受信任的页面使用(不要对外部网页暴露)。
- Android 4.2+ 必须使用
@JavascriptInterface注解。
🧩 三、方式二:shouldOverrideUrlLoading()(常用于拦截 URL)
✅ 原理
H5 页面通过 改变 URL(Scheme) 传递参数,Android 在 WebViewClient 中拦截 URL,解析指令执行原生逻辑。
属于 URL Scheme 通信机制。
📘 使用示例
1️⃣ H5 调用方式(JS)
// 通过改变URL触发原生拦截
window.location.href = "jsbridge://showToast?msg=来自H5的问候";
2️⃣ Android 拦截解析
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
if (url != null && url.startsWith("jsbridge://")) {
val uri = Uri.parse(url)
val action = uri.host
val msg = uri.getQueryParameter("msg")
when (action) {
"showToast" -> Toast.makeText(view?.context, msg ?: "", Toast.LENGTH_SHORT).show()
"openPage" -> { /* 打开原生页面逻辑 */ }
}
return true // 拦截,不让WebView继续加载
}
return super.shouldOverrideUrlLoading(view, url)
}
}
⚙️ 优点
✅ 简单直观,适配所有 Android 版本。
✅ 易于调试(直接通过 URL 查看指令)。
✅ 不存在 @JavascriptInterface 的安全隐患。
⚠️ 缺点
❌ 无法直接返回结果(单向通信)。
❌ 参数传递复杂(需手动 URL 编解码)。
❌ 大量通信时性能较差(频繁 loadUrl)。
❌ H5 改变 location.href 会导致页面跳转或刷新。
🧩 四、方式三:evaluateJavascript()(Android → H5,异步调用)
✅ 原理
Android 通过执行 JS 代码的方式调用 H5 方法,并可接收返回值(Android 4.4+ 支持)。
📘 示例
webView.evaluateJavascript("javascript:showMessage('Hello from Android')") { result ->
Log.d("JS_CALLBACK", "H5 返回: $result")
}
H5 端 JS:
function showMessage(msg) {
alert("JS 收到:" + msg);
return "OK";
}
⚙️ 优点
✅ 异步执行,不会阻塞 UI 线程。
✅ 可获取 JS 执行结果。
✅ 安全性高,无需注入对象。
⚠️ 缺点
❌ 仅 Android 4.4+ 支持。
❌ 调用 JS 代码需拼接字符串,易出错。
❌ 调试稍不便。
🧩 五、三种方式对比总表
| 类型 | 调用方向 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| addJavascriptInterface | H5 → Android | JS 调原生对象方法 | 正规、高兼容、支持返回值 | 旧版有安全风险 | 双向通信、需要数据交互 |
| shouldOverrideUrlLoading | H5 → Android | 拦截 URL Scheme | 兼容性高、安全、易调试 | 无返回值、参数需解析 | 简单事件调用,如跳转、Toast |
| evaluateJavascript | Android → H5 | 执行 JS 代码 | 异步安全、支持回调 | 仅 4.4+ 支持 | 原生调用 JS、获取返回值 |
🧰 六、实战建议(最佳实践)
| 场景 | 推荐方案 | 说明 |
|---|---|---|
| 简单调用(H5 调原生功能) | shouldOverrideUrlLoading | 用 URL Scheme 定义协议,简单直观 |
| 双向交互(需传参或回调) | addJavascriptInterface + evaluateJavascript 组合 | 官方推荐写法,数据安全 |
| H5 调原生高频事件(如扫码、登录) | addJavascriptInterface | 性能更优,返回值支持 |
| 兼容旧 WebView | shouldOverrideUrlLoading | 不依赖 JS 注入机制 |
💡 七、综合总结
| 特性 | addJavascriptInterface | shouldOverrideUrlLoading | evaluateJavascript |
|---|---|---|---|
| 调用方向 | JS → Android | JS → Android | Android → JS |
| 返回值支持 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
| 安全性 | ⚠️ Android <4.2 不安全 | ✅ 安全 | ✅ 安全 |
| 兼容性 | Android 4.2+ | 所有版本 | Android 4.4+ |
| 性能 | 中等 | 慢(需跳转) | 快(异步) |
| 使用难度 | 中 | 简单 | 简单 |
| 典型场景 | 支付、扫码、数据交互 | 点击跳转、Toast 提示 | 原生调用 JS 方法 |
🧩 八、综合使用建议
🧠 实际应用建议
| 场景 | 推荐方式 |
|---|---|
| 简单通知类交互(如关闭页面、提示) | shouldOverrideUrlLoading |
| 复杂业务调用(如支付、分享、登录) | addJavascriptInterface |
| 原生主动触发 JS(如刷新页面数据) | evaluateJavascript |
| 高安全需求的混合页面 | 自定义协议 + 加密通信 + 校验签名 |
🔐 安全第一,通信第二。
在真实项目中,建议配合统一的 JSBridge 框架(如 WebViewJavascriptBridge)进行封装,统一协议、加密通信,提升可维护性与安全性。
✅ 推荐组合方案:
- JS 调用原生 →
addJavascriptInterface- 原生回调 JS →
evaluateJavascript- 简单点击或跳转 →
shouldOverrideUrlLoading
🔧 实战总结代码结构
class WebViewBridge(context: Context, private val webView: WebView) {
@JavascriptInterface
fun showToast(msg: String) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
@JavascriptInterface
fun getUserInfo(): String {
return "{"name":"Jeled","id":123}"
}
// 原生回调 JS 方法
fun notifyJs(message: String) {
webView.evaluateJavascript("javascript:onNativeMessage('$message')", null)
}
}
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(WebViewBridge(this, webView), "AndroidBridge")
// H5 调用原生
AndroidBridge.showToast("来自H5的问候");
// 原生回调H5
function onNativeMessage(msg) {
console.log("收到原生消息:" + msg);
}
✨ 总结:
| 类型 | 特点 |
|---|---|
addJavascriptInterface | 功能最强,但要防漏洞 |
shouldOverrideUrlLoading | 简单安全,推荐入门使用 |
evaluateJavascript | 现代 API,适合原生调用 JS |
addJavascriptInterface—— 官方正统,功能强大。
shouldOverrideUrlLoading—— 简单安全,适合轻交互。
evaluateJavascript—— 原生主动调用 H5 的利器。