JSBridge
JSBridge
简介
JavaScript Bridge(JSBridge)是一种连接原生代码和JavaScript代码的技术,它允许在Web视图(如WebView)和原生应用程序之间进行双向通信。通过JSBridge,可以在JavaScript和原生代码之间传递数据、调用方法,并处理回调。
作用
- 实现双向通信: 允许JavaScript代码调用原生代码的方法,同时也允许原生代码调用JavaScript代码的函数。
- 传递数据: 可以在JavaScript和原生代码之间传递各种类型的数据,如字符串、数字、对象等。
- 处理回调: 支持在调用完成后处理回调,使得在异步操作完成后能够获取到结果。
实现原理
通过 WebView 提供的接口实现 JavaScript 和原生代码之间的通信。在移动应用开发中,通常使用 WebView 来加载网页或者 web 应用,而 JSBridge 技术则可以让这些网页或者 web 应用调用设备的原生功能,同时也可以让原生代码调用网页中的 JavaScript 函数。
注册消息处理器(Message Handler): 在原生代码中注册一个消息处理器,用于接收 JavaScript 发送的消息。在 iOS 中,常用的是通过 WebView 的配置对象(例如 UIWebViewConfiguration 或 WKWebViewConfiguration)的 userContentController 属性,调用其 add(_:name:) 方法添加一个名为 "messageHandler"(或其他自定义的名称)的消息处理器。在 Android 中,可以通过 addJavascriptInterface() 方法将原生对象暴露给 JavaScript。
JavaScript 调用原生代码: JavaScript 通过特定的接口调用原生代码,并将需要传递的数据作为参数传入。在 iOS 中,通常是通过 WebView 的 window.webkit.messageHandlers 对象调用原生代码,例如 window.webkit.messageHandlers.messageHandler.postMessage(data),其中 "messageHandler" 是原生代码注册的消息处理器名称,data 是需要传递的数据。在 Android 中,可以通过 JavaScript 中的 window. 对象调用原生暴露的接口。
原生代码接收消息并处理: 原生代码通过之前注册的消息处理器接收 JavaScript 发送的消息,在 iOS 中,需要实现 WKScriptMessageHandler 协议的 userContentController(:didReceive:) 方法,或者在旧版的 UIWebView 中,需要实现 UIWebViewDelegate 协议的 webView(:shouldStartLoadWith:request:navigationType:) 方法,在该方法中判断并处理 JavaScript 发送的消息。
原生代码调用 JavaScript 函数: 原生代码处理完逻辑后,有时需要将结果返回给 JavaScript 端。在 iOS 中,可以通过 WebView 的 evaluateJavaScript(_:completionHandler:) 方法执行 JavaScript 代码,将处理结果传递给 JavaScript 函数。
JavaScript 接收并处理原生代码返回的结果: JavaScript 在收到原生代码的返回结果后,可以在回调函数中处理这些结果,完成整个通信流程。
前端使用React脚手架搭建网页
npx create-react-app my-app --template typescript
IOS端使用Swift 写代码加载网页
// 创建 WKWebViewConfiguration 实例以配置 WKWebView
let webConfiguration = WKWebViewConfiguration()
// 添加消息处理程序以接收来自 Web 内容的消息
webConfiguration.userContentController.add(self, name: "saveImage")
// 创建 WKWebView 实例
webView = WKWebView(frame: self.view.frame, configuration: webConfiguration)
// 将 WebView 添加到视图中
self.view.addSubview(webView)
// 加载指定的 URL
if let url = URL(string: "http://192.168.220.81:3000") {
let request = URLRequest(url: url)
webView.load(request)
}
IOS端定义保存图片到相册的方法
// 接收来自 Web 内容的消息并进行处理
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// 检查消息名称是否为 "saveImage",并尝试提取 Base64 编码的图像数据
if message.name == "saveImage", let base64String = message.body as? String {
// 打印接收到的来自 Web 的消息
print("Received message from web: \(base64String)")
// 将 Base64 编码的图像数据保存到相册
saveBase64ImageToAlbum(base64String: base64String)
}
}
// 将 Base64 编码的图像数据保存到相册
func saveBase64ImageToAlbum(base64String: String) {
// 将 Base64 字符串解码为图像数据
if let imageData = Data(base64Encoded: base64String) {
// 将图像数据转换为 UIImage
if let image = UIImage(data: imageData) {
// 将图像保存到相册
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
}
}
开启访问相册的权限

前端调用IOS端的方法
- 传递base64格式图片给IOS端(图片前缀删掉,这一步骤要么前端处理要么IOS端处理,建议前端出问题好排查)
import React, { useEffect, useState } from "react"
import { Button, Toast } from "antd-mobile"
const WebJsBridge = () => {
const [image] = useState<string>(``)
// 保存图片到相册的函数
const handleSave = () => {
window.webkit.messageHandlers.saveImage.postMessage(image)
}
return (
<div>
<h1>WebJsBridge</h1>
<img
style={{ width: "200px", height: "500px" }}
src={`data:image/png;base64,${image}`}
alt="图片"
/>
<Button onClick={handleSave}>保存图片到相册</Button>
</div>
)
}
export default WebJsBridge
// image放base64格式的图片
图片转base64格式网站

前端触发保存图片的事件


IOS端图片保存的状态告知网页端
// 图像保存到相册后的回调方法
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
var message: String
// 检查是否有错误发生
if let error = error {
// 如果出现错误,将错误信息记录下来
message = "保存失败,错误:\(error.localizedDescription)"
} else {
// 如果保存成功,设置成功信息
message = "success"
}
// 构建 JavaScript 脚本,用于向网页发送消息,通知保存状态
let jsScript = "window.BridgeInterface.handleSaveResult('\(message)');"
// 执行 JavaScript 脚本,向网页发送消息
webView.evaluateJavaScript(jsScript, completionHandler: nil)
}
// 处理保存结果的函数
const handleSaveResult = (resultMessage: any) => {
console.log(`🍍🙏🍍👉: resultMessage`, resultMessage)
Toast.show({
icon: "success",
content: "保存成功",
})
}
useEffect(() => {
window.BridgeInterface = {
handleSaveResult,
}
}, [])

