跳转至

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 可以调用,例如:

  • takePhoto
  • getUserToken
  • openSettings

"在初始化期间注册"意味着:

  • 一旦 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–原生桥接(经由 addJavascriptInterfacepostMessage 或自定义 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)通信以实现:

  1. 注册 BLE 事件的回调
  2. 开始扫描 BLE 设备
  3. 使用 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) 调用原生函数,并可选地接收异步响应。

总体流程如下:

  1. JS 侧注册处理器,以便原生代码可以通知网页。
  2. JS 告知原生代码开始扫描 BLE 设备。
  3. 发现设备时,原生层回调 JS。
  4. 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.namedevice.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);
      
  • 使用方式:

    • 这是将 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.successtrue,则记录:

    console.log("Connecting...");
    

实际连接建立后,原生侧应触发步骤 1 中注册的 bleConnectSuccessCallBack 处理器。

端到端流程摘要

  1. 注册回调

    • findBleDeviceCallBack:发现设备时由原生调用。
    • bleConnectSuccessCallBack:连接成功时由原生调用。
  2. 开始扫描

    • JS 调用 startBleScan
    • 原生开始扫描,成功时返回 { success: true }
    • 发现设备时,原生反复调用 findBleDeviceCallBack 并传入设备数据。
  3. 选择并连接

    • JS 决定连接哪个设备(示例中为:5 秒后使用硬编码的 MAC 地址)。
    • JS 使用选择的 MAC 地址调用 connBleByMacAddress
    • 原生尝试连接,如果连接过程正常启动则回复 { success: true }
    • 完全连接后,原生触发 bleConnectSuccessCallBack 并传入设备的 MAC 地址。 您可以将日志替换为 UI 更新(在列表中显示设备、显示连接状态等),并通过检查 result.success === false 并显示适当的消息来添加错误处理。