WebView 初始化¶
1. "JavaScript 已启用。"¶
WebView 的 javaScriptEnabled = true(Android)或等效设置已开启,因此加载页面内的 JS 代码可以正常运行。
如果不启用此项,HTML 中的 <script> 标签和任何 JS 逻辑都将被 WebView 完全忽略。
启用 JS 是以下操作所必需的:
- 从 JS 调用原生桥接函数。
- 处理点击事件、动态 UI 更新、AJAX/fetch 调用等。
安全注意事项:
- 仅为您信任或控制的页面启用 JS(如您自己应用的 HTML,而非任意不受信任的 URL)。
- 结合其他限制措施(如限制 URL 加载、内容安全策略)使用。
2. "已附加安全的 WebViewClient/WebChromeClient。"¶
WebViewClient:
- 控制 URL 的加载方式(在应用内打开还是在外部浏览器中打开)。
- 允许拦截和验证导航(如阻止未知域名、深度链接)。
- 适合执行诸如"只允许 https://my-app-domain.com"之类的规则。
WebChromeClient:
- 处理高级浏览器特性:
- JavaScript 对话框(alert、confirm、prompt)
- 控制台日志
- 权限申请(摄像头、地理位置等)
-
提供钩子以批准/拒绝 JS 尝试执行的操作。
-
此处"安全"意味着您不仅仅使用默认行为:
- 您重写方法以:
- 在加载前验证目标 URL。
- 如非必要,阻止混合内容或明文 HTTP。
- 控制文件上传、摄像头或其他敏感功能。
除非明确需要,否则禁用或限制危险功能(如任意文件访问、从文件 URL 进行通用访问等)。
3. "所有桥接处理器均在初始化期间注册。"¶
您的原生桥接暴露了特定的"处理器"(端点),JS 可以调用,例如:
takePhotogetUserTokenopenSettings
"在初始化期间注册"意味着:
- 一旦 WebView(或桥接模块)创建,您即调用类似以下的代码:
bridge.registerHandler("takePhoto", handlerFunction)
- 在页面加载完成时,所有预期的处理器均已存在。
这样做的重要性:
- 避免竞态条件(JS 调用尚未注册的处理器 → 出现"处理器未找到"等错误)。
- 在 JS 与原生之间建立清晰的契约:
- 可用 API 集合预先确定。
- 可以对其进行文档记录和版本管理。
安全优势:
- 只注册您明确想要暴露的内容。
- 不会意外地将随机原生方法开放给 JS。
- 可以在每个处理器内部添加身份验证/权限检查。
4. "JS 可以使用 window.bridge.callHandler(...) 调用原生方法。"¶
这是 JS 用于与原生代码通信的 API 接口。 典型调用形式:
window.bridge.callHandler("takePhoto", { quality: "high" }, function (response) { /* handle result */ });
底层机制:
callHandler将处理器名称和数据打包为消息。- 通过 JS–原生桥接(经由
addJavascriptInterface、postMessage或自定义 URL Scheme,具体取决于实现)发送消息。 - 原生代码接收消息,查找已注册的处理器并执行,然后发送响应,触发 JS 回调。
设计优势:
- 从 JS 到原生的单一入口点,而非暴露许多随机全局变量。
- 易于日志记录、审计和验证:
- 可以在原生侧记录每次
callHandler调用及其参数。 - 良好地支持异步操作:原生可稍后完成并通过 JS 回调传递成功/错误结果。
注册桥接处理器¶
处理器注册方式如下:
bridgeWebView.registerHandler("mqttConnect", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
// native code
}
});
此代码行的概念说明¶
-
注册一个命名的桥接 API
"mqttConnect"是处理器名称(JS 将调用的 API 名称)。- 您在告诉桥接:
"当 JS 调用 mqttConnect 时,执行此 Java 代码。"
-
连接 JS 世界 → 原生 MQTT 逻辑:
- 此处理器用于 MQTT 连接逻辑(如连接到代理、身份验证等)。
- 所有繁重的工作(套接字、凭据、重试等)保留在原生代码中,而非在 JS 中。
扫描并连接到 BLE 设备¶
此代码片段展示了 WebView JavaScript 代码如何通过 bridgeWebView 与原生层(Android/iOS)通信以实现:
- 注册 BLE 事件的回调
- 开始扫描 BLE 设备
- 使用 MAC 地址连接到设备
// Step 1: Register callbacks
bridgeWebView.registerHandler("findBleDeviceCallBack", function(data) {
const device = JSON.parse(data);
console.log("Found:", device.name, device.macAddress);
});
bridgeWebView.registerHandler("bleConnectSuccessCallBack", function(macAddress) {
console.log("Connected to:", macAddress);
});
// Step 2: Start scanning
bridgeWebView.callHandler("startBleScan", "", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Scanning started...");
}
});
// Step 3: Connect to device (when you find the one you want)
setTimeout(() => {
bridgeWebView.callHandler("connBleByMacAddress", "AA:BB:CC:DD:EE:FF", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Connecting...");
}
});
}, 5000);
概述¶
bridgeWebView是在 WebView 内部暴露的 JavaScript–原生桥接对象。registerHandler(name, callback)注册一个 JavaScript 函数,供原生应用调用。callHandler(name, data, callback)调用原生函数,并可选地接收异步响应。
总体流程如下:
- JS 侧注册处理器,以便原生代码可以通知网页。
- JS 告知原生代码开始扫描 BLE 设备。
- 发现设备时,原生层回调 JS。
- JS 决定连接哪个设备,并调用原生"连接"函数。
步骤 1:从原生到 JS 的回调注册¶
findBleDeviceCallBack
bridgeWebView.registerHandler("findBleDeviceCallBack", function(data) {
const device = JSON.parse(data);
console.log("Found:", device.name, device.macAddress);
});
- 目的:每当扫描过程中发现 BLE 设备时,此处理器由原生层触发。
-
参数:
data:来自原生代码的 JSON 字符串。期望表示一个设备对象,例如:{ "name": "My BLE Device", "macAddress": "AA:BB:CC:DD:EE:FF" }
-
逻辑:
-
JSON.parse(data)将 JSON 字符串转换为 JavaScript 对象。 - 从解析后的对象中读取
device.name和device.macAddress。 - 记录设备信息:
console.log("Found:", device.name, device.macAddress);
-
使用方式:
- 之后可以将
console.log替换为 UI 更新,例如将设备添加到列表供用户选择。
- 之后可以将
bleConnectSuccessCallBack
bridgeWebView.registerHandler("bleConnectSuccessCallBack", function(macAddress) {
console.log("Connected to:", macAddress);
});
- 目的:通知 JS 侧 BLE 连接已成功建立。
-
参数:
macAddress:包含已连接设备 MAC 地址的简单字符串。
-
逻辑:
- 记录成功连接的设备的 MAC 地址:
console.log("Connected to:", macAddress);
- 记录成功连接的设备的 MAC 地址:
-
使用方式:
- 这是将 UI 更新为"已连接"状态或启用需要活跃 BLE 连接的功能的好时机。
步骤 2:开始 BLE 扫描¶
bridgeWebView.callHandler("startBleScan", "", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Scanning started...");
}
});
- 目的:请求原生层开始扫描 BLE 设备。
-
参数:
- 第一个参数:
"startBleScan"— 原生方法名称。 - 第二个参数:
""— 空字符串;此调用无需额外参数。 - 第三个参数:用于处理原生响应的回调函数。
- 第一个参数:
-
响应处理:
response期望为 JSON 字符串,例如:{ "success": true }
-
解析为 JavaScript 对象:
const result = JSON.parse(response); -
如果
result.success为 true,表示扫描已成功启动:if (result.success) { console.log("Scanning started..."); } -
流程:
- 扫描启动后,原生侧通常会在发现设备时不断调用
findBleDeviceCallBack。
- 扫描启动后,原生侧通常会在发现设备时不断调用
步骤 3:通过 MAC 地址连接到设备¶
setTimeout(() => {
bridgeWebView.callHandler("connBleByMacAddress", "AA:BB:CC:DD:EE:FF", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Connecting...");
}
});
}, 5000);
- 目的:使用 MAC 地址连接到特定 BLE 设备。
-
setTimeout:
setTimeout(..., 5000);将此调用延迟 5 秒。- 这模拟了:"等待一段时间进行扫描和发现设备,然后尝试连接"。
- 在实际应用中,您可能会在用户从列表中选择设备后调用此函数(而不是使用固定超时)。
callHandler 的参数:
"connBleByMacAddress":负责连接到设备的原生方法。"AA:BB:CC:DD:EE:FF":目标设备的 MAC 地址(占位符;应替换为来自findBleDeviceCallBack或用户选择的实际设备 MAC)。function(response) { ... }:处理原生响应的回调。
响应处理:
- response 期望为 JSON 字符串,例如:
{ "success": true }
-
解析:
const result = JSON.parse(response); -
如果
result.success为true,则记录:console.log("Connecting...");
实际连接建立后,原生侧应触发步骤 1 中注册的 bleConnectSuccessCallBack 处理器。
端到端流程摘要¶
-
注册回调
findBleDeviceCallBack:发现设备时由原生调用。bleConnectSuccessCallBack:连接成功时由原生调用。
-
开始扫描
- JS 调用
startBleScan。 - 原生开始扫描,成功时返回
{ success: true }。 - 发现设备时,原生反复调用
findBleDeviceCallBack并传入设备数据。
- JS 调用
-
选择并连接
- JS 决定连接哪个设备(示例中为:5 秒后使用硬编码的 MAC 地址)。
- JS 使用选择的 MAC 地址调用
connBleByMacAddress。 - 原生尝试连接,如果连接过程正常启动则回复
{ success: true }。 - 完全连接后,原生触发
bleConnectSuccessCallBack并传入设备的 MAC 地址。 您可以将日志替换为 UI 更新(在列表中显示设备、显示连接状态等),并通过检查result.success === false并显示适当的消息来添加错误处理。