JS 和 Android
- 原生调用 JS
4.4 版本之前
java
// mWebView = new WebView(this); //当前webview对象
// 通过loadUrl方法进行调用 参数通过字符串的方式传递
mWebView.loadUrl("javascript: 方法名('参数1,参数2...')");
//也可以在UI线程中运行
runOnUiThread(new Runnable() {
@Override
public void run() {
// 通过loadUrl方法进行调用 参数通过字符串的方式传递
mWebView.loadUrl("javascript: 方法名('参数1,参数2...')");
// 安卓中原生的弹框
Toast.makeText(Activity名.this, "调用方法...", Toast.LENGTH_SHORT).show();
}
});
4.4 版本之后
java
// 通过异步的方式执行js代码,并获取返回值
mWebView.evaluateJavascript("javascript: 方法名('参数1,参数2...')", new ValueCallback<String>() {
@Override
// 这个方法会在执行完毕之后触发, 其中value就是js代码执行的返回值(如果有的话)
public void onReceiveValue(String value) {
}
});
- JS 调用Android
安卓配置:
java
// Android4.2版本以上,本地方法要加上注解@JavascriptInterface,否则无法使用
private Object getJSBridge(){
// 实例化新对象
Object insertObj = new Object(){
@JavascriptInterface
// 对象内部的方法1
public String foo(){
// 返回 字符串 foo
return "foo";
}
@JavascriptInterface
// 对象内部的方法2 需要接收一个参数
public String foo2(final String param){
// 返回字符串foo2拼接上传入的param
return "foo2:" + param;
}
};
// 返回实例化的对象
return insertObj;
}
// 获取webView的设置对象,方便后续修改
WebSettings webSettings = mWebView.getSettings();
// 设置Android允许JS脚本,必须要!!!
webSettings.setJavaScriptEnabled(true);
// 暴露一个叫做JSBridge的对象到webView的全局环境
mWebView.addJavascriptInterface(getJSBridge(), "JSBridge");
在 web 页面中调用
js
//调用方法一
window.JSBridge.foo(); //返回:'foo'
//调用方法二
window.JSBridge.foo2('test');//返回:'foo2:test'
JS 和 IOS
- 原生调用 JS
swift
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
// 加载完毕会触发(类似于Vue的生命周期钩子)
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// 类似于console.log()
print("触发啦");
// wkWebView调用js代码,其中doSomething()会被当做js解析
webView.evaluateJavaScript("doSomething()");
}
}
- JS 调用 IOS
- JS 部分
js
window.webkit.messageHandlers.方法名.postMessage(数据)
- iOS 部分注册监听
swift
wkWebView.configuration.userContentController.add(self, name: 方法名)
- iOS 部分遵守协议相关方法
swift
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// message.body 就是传递过来的数据
print("传来的数据为", message.body)
}
url scheme(互通协议)
web 调用
js
<input class="ios" type="button" value="使用iframe加载url">
// 加载url 通过iframe 设置URL 目的是让ios拦截
function loadUrl(url) {
// 创建iframe
const iframe = document.createElement('iframe');
// 设置url
iframe.src = url;
// 设置尺寸(不希望他被看到)
iframe.style.height = 0;
iframe.style.width = 0;
// 添加到页面上
document.body.appendChild(iframe);
// 加载了url之后他就没用了
// 移除iframe
iframe.parentNode.removeChild(iframe);
}
document.querySelector('.ios').onclick = function () {
loadUrl('taobao://click');
}
IOS 监听
js
// 拦截url
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// 获取url
let url = navigationAction.request.url?.absoluteString;
if(url=="taobao://click"){
print("调用系统功能");
decisionHandler(.cancel);
}else{
decisionHandler(.allow);
}
}
HyBridApp

- 开发框架
- 提供前端运行环境
- 实现前端和原生交互
- 封装原生功能,提供插件机制
加载优化
- 骨架屏
html
<style>
.shell .placeholder-block{
display: block;
height: 5em;
background: #ccc;
margin: 1em;
}
.novel {
height: 5em;
background-color: yellowgreen;
}
</style>
</head>
<body>
<div class="shell">
<div class="placeholder-block"></div>
</div>
</body>
</html>
<script>
setTimeout(()=>{
// 移除 占位dom元素
document.querySelector('.shell').innerHTML = ''
// 创建数据的dom元素 添加到页面上
let p = document.createElement('p')
p.innerHTML = '黑马程序员'
p.className = 'novel'
document.querySelector('.shell').appendChild(p)
},3000)
</script>
webview
java
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 创建webView
var webView = WKWebView(frame: self.view.bounds)
// 设置自己为WebView的代理
webView.navigationDelegate = self
// 添加到页面上
self.view.addSubview(webView)
// 创建URL对象
var url = URL(string: "https://www.baidu.com")
// 创建URLRequest对象
var request = URLRequest(url: url!)
// 加载URL
webView.load(request)
}
}
JSBridge

- 设计思想
- JS 向原生发送消息
- 原生向 JS 发送消息
javascript
window.JSBridge = {
invoke: function(action, params, callback) {
// 生成唯一回调ID
const callbackId = 'cb_' + Date.now();
// 存储回调函数
window[callbackId] = callback;
// 构建标准化消息
const msg = {
action: action,
params: params || {},
callbackId: callbackId
};
// 根据平台调用不同原生桥
if (isIOS()) {
window.webkit.messageHandlers.nativeBridge.postMessage(JSON.stringify(msg));
} else if (isAndroid()) {
window.android.postMessage(JSON.stringify(msg));
}
},
// 原生调用此方法来回调结果
receiveMessage: function(msg) {
const { callbackId, result, error } = msg;
const callback = window[callbackId];
if (callback) {
if (error) {
callback(null, error); // 错误回调
} else {
callback(result, null); // 成功回调
}
// 执行后删除回调,避免内存泄漏
delete window[callbackId];
}
}
};
// 使用示例:调用原生相机
JSBridge.invoke('takePhoto', { quality: 'high' }, (result, error) => {
if (error) {
console.error('拍照失败:', error);
} else {
console.log('照片路径:', result.imagePath);
}
});
解释:
-
前端调用
JSBridge.invoke
时:存储回调函数,生成唯一的callbackId
(如cb_1725000000000
),确保每个回调能被唯一识别;把回调函数挂载到window
对象上 (即window[callbackId] = 回调函数
),相当于 "暂时存档",避免函数被垃圾回收。 -
前端向原生发送 "带回调 ID 的消息",然后根据平台(iOS/Android)把消息发给原生,此时原生收到的是 "操作指令 + 回调 ID"
-
原生执行操作(如调用相机),原生接收到消息后,解析出
action
和params
,执行对应的原生逻辑
- iOS:调用
UIImagePickerController
(系统相机接口),按quality: 'high'
配置拍照质量; - Android:调用
Camera
或CameraX
接口,同样按参数执行拍照。 这个阶段完全在原生环境(Objective-C/Swift 或 Java/Kotlin)中运行,与前端 JS 无关。
- 原生将 "结果 + 回调 ID" 回传给前端
原生执行完操作后(无论成功 / 失败),会构建一个 "结果消息",包含:callbackId: 'cb_1725000000000'
(必须和前端传过来的一致,才能找到对应的回调);result: { imagePath: '/var/mobile/.../photo.jpg' }
(成功时的结果,如照片路径); 或error: '用户取消拍照'
(失败时的错误信息)。
然后原生会主动调用前端 JSBridge 预留的 receiveMessage
方法,把 "结果消息" 传回去。
- 前端
receiveMessage
执行回调函数
-
解析原生传过来的消息,提取
callbackId
、result
、error
; -
通过
callbackId
找到之前挂载在window
上的回调函数(即window['cb_1725000000000']
); -
执行回调函数:
- 成功:调用
callback(result, null)
(如打印照片路径); - 失败:调用
callback(null, error)
(如打印 "用户取消拍照");
- 成功:调用
-
执行完后删除
window[callbackId]
,避免内存泄漏。
到这一步,回调函数才真正在前端 JS 环境中执行,完成整个跨端通信闭环。