HarmonyOS 鸿蒙NEXT实现H5 JSBridge方案

HarmonyOS 鸿蒙NEXT实现H5 JSBridge方案

背景

通常在移动端项目中涉及到H5与Native的通信,基本的通用方案是通过客户端去注入JS到网页中,然后拦截H5发起的自定义scheme请求来实现通信,当然还有在iOS的webkit通过WKScriptMessageHandler协议来实现与JS交互,它允许应用程序从js接收postMessage消息,通过执行evaluateJavaScript回传消息到H5,但这种方式不再支持UIWebView,所以我们这里对此种方式不做赘述,只对通过使用 iframe 创建消息队列通信方式进行介绍。

常用三方库

开源框架:WebViewJavascriptBridgeJockey

本文是对H5和原生侧各自实现Jockey的介绍,包括代码层面的实现细节以及在鸿蒙项目中的具体应用场景和用法。

H5侧实现

首先向window中添加一个Jockey对象,对象内部初始化消息队列来实现双端通信

Jockey.dispatchers.push(nativeDispatcher);
Jockey.dispatchers.push(IframeDispatcher);
window.addEventListener("message", $.proxy(Jockey.onMessageRecieved, Jockey), false);
window.Jockey = Jockey;

在H5侧实现中通过监听和发送消息来进行H5向Native发起请求和接收Native消息的处理,概括为下面两种方式:

  • Jockey.on('getInfoCallback', off);
  • Jockey.send('getInfo', params);

下面分开介绍具体实现,首先是Jockey中H5接受Native回调:

var Jockey = {
    listeners: {}, // H5侧维护的消息队列
    messageCount: 0, // 用来发送和接受native回调的标识符
    
    // 监听来自native的发送消息,type为具体事件,fn为回调函数
    // H5会将所有需native侧回调的事件放入listeners队列中
    on: function(type, fn) {
      
    },
    
    // 接受来自native的回调,并触发listeners监听事件回调H5,
    // 当此type的所有回调执行完成后调用complete通知native(调用的是下列nativeDispatcher中sendCallback方法)
    trigger: function(type, messageId, json) {
     
    }
}

H5向Native发起请求

Jockey中H5向Native发起请求部分

同时messageCount作为后续接受native回调callbacks的标识符,dispatcher.send最终会调用到nativeDispatcher中的send方法

send: function(type, payload, complete) {
    if (payload instanceof Function) {
        complete = payload;
        payload = null;
    }
    payload = payload || {};
    complete = complete || function() {};
    var envelope = this.createEnvelope(this.messageCount, type, payload);
    this.dispatchers.forEach(function(dispatcher) {
        dispatcher.send(envelope, complete);
    });
    this.messageCount += 1; // 每发送一次消息加1
},

创建 iframe

nativeDispatcher实现,主要是向native发送消息和接受回调的逻辑:

var nativeDispatcher = {
    callbacks: {},
    // 向native主动发起通信,envelope是消息体,complete会存在H5侧维护的消息队列中
    send: function(envelope, complete) {
        this.dispatchMessage("event", envelope, complete);
    },
     
    },
    // 发送消息核心实现
    dispatchMessage: function(type, envelope, complete) {

      var src = "jockey://" + ...
        var iframe = document.createElement("iframe");
        iframe.setAttribute("src", src);
        document.documentElement.appendChild(iframe);
        iframe.parentNode.removeChild(iframe);
        iframe = null;
    }
}

上述代码中主要有两点:

  1. H5向Native发送消息,是通过创建 iframe 发起了一个src请求,特殊的是请求的URL自定义了scheme(jockey://)用来客户端区分请求类型进行拦截。
  2. H5在发送和接受消息时维护了listeners和callbacks两个消息队列,分别用来接受Native侧主动发送的消息和回调

之后就是Native拦截自定义scheme请求以及向H5发送请求,实现Native向H5通信并接收H5消息回传。

鸿蒙侧实现

清楚了H5侧的实现,在鸿蒙侧也只需要按流程重新实现一边即可,需要关注的是如何拦截H5请求以及如何向H5发送消息。

// 监听H5回调
onCallback() {
  this.Jockey?.on('getInfo, {
    payload: () => {
    }
     jockeyRetrun: () => {
       let params: string = JSON.stringify(dataParams)
       return params
     }
  })
}

sendPfo(perform?: (returnStr: string) => void) {

  let params: string = JSON.stringify(dataParams)
  this.Jockey?.send('getInfoCallBack', params, (returnStr) => {
    console.log(returnStr)
    if (returnStr) {
      perform?.(returnStr)
    }
  })
}

Jockey.on 会在Jockey内添加listeners监听消息队列,来接受来自H5的回调

public on(type: string, handler: JockeyAsyncHandler) {
  let listenerList = this.listeners.get(type)
  if (listenerList) {
    listenerList.push(handler)
  } else {
    listenerList = [handler]
    this.listeners.set(type, listenerList)
  }
}

Jockey.send 触发Jockey内WebviewController执行runJavaScriptExt脚本来调用H5内trigger函数,

传递的参数包括type事件、messageId以及消息数据,其中messageId是维护在Native侧来接受H5回调的标识符,保存在callbacks消息队列中

triggerCallbackForMessage是拦截H5请求后,触发callbacks消息队列接受H5侧消息回传,至此已完成Native向H5通信并接受H5消息回传。

trigger实现:

public send(type: string, params: string, perform?: (returnStr: string) => void) {
  let messageId = this.messageCount.toString()
  if (perform) {
    this.callbacks.set(messageId.toString(), perform)
  }
  let js = ``

  try {
    // 异步执行JavaScript脚本,并通过Promise方式返回脚本执行的结果
    this.controller.runJavaScriptExt(
      js,
      (error, result) => {
        if (error) {
          let e: business_error.BusinessError = error as business_error.BusinessError
          console.error(`ErrorCode: ${e.code},  Message: ${e.message}`)
          return
        }
        this.messageCount += 1
        if (result) {}
      })
  } catch (error) {
    let e: business_error.BusinessError = error as business_error.BusinessError
    console.error(`ErrorCode: ${e.code},  Message: ${e.message}`)
  }
}
// 拦截H5请求后,触发callbacks消息队列接受H5给Native回调
private triggerCallbackForMessage(messageId: string, params: string) {
}

WebComponent

其中WebComponent是通用的web容器,外部调用方式也很简单:

WebComponent({webUrl: this.webUrl})

组件内部会自动绑定DUJockey到当前web,通过WebviewController可以控制Web组件各种行为,

一个WebviewController对象只能控制一个Web组件,且必须在Web组件和WebviewController绑定后,才能调用WebviewController上的方法,我们后续想H5发送消息就需要通过WebviewController来实现。

private webviewController: web_webview.WebviewController = new web_webview.WebviewController()

aboutToAppear(): void {
  this.duJockey = new DUJockey(this.webviewController)
}

WebComponent内部会加载web组件并管理其生命周期,在这里实现web各种加载状态和回调。

其中onLoadIntercept就是Web组件加载url时触发的回调,用于判断是否阻止此次访问

相当于iOS decidePolicyForNavigationAction 和Android的 shouldOverrideUrlLoading 拦截请求方法,在这里根据scheme判断是否是来自Jockey请求

onLoadIntercept方法返回 return 为ture表示拦截成功,不执行后面的跳转操作。而false表示按正常流程执行。拦截成功后我们从url中获取data,接着就可以按照我们自己的需求去处理了。

public hookUrl(url: string): boolean {

}

如果在Native侧listeners有对应的消息监听,会触发消息回传到Native,

同时如果Native需要回调结果给H5,在接收了该事件下所有H5消息后会触发triggerCallbackOnWebView回调到H5

private triggerEventFromWebView(params: string) {
  
}

到这里已实现Native接受来自H5的消息并回调H5,整个流程全部跑通。


更多关于HarmonyOS 鸿蒙NEXT实现H5 JSBridge方案的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

鸿蒙NEXT实现H5 JSBridge方案步骤:

  1. 使用鸿蒙的Web组件加载H5页面
  2. 通过@ohos.web.webview提供的能力注册JavaScriptProxy对象
  3. H5调用Native方法使用window.harmonyWebview.postMessage()
  4. Native调用H5方法使用webController.runJavaScript()
  5. 双向通信需在Web组件配置fileAccess和javaScriptProxy属性

关键代码示例:

// 注册JSBridge
webview.javaScriptProxy = {
  object: {
    nativeMethod: (arg) => {
      // 处理H5调用
    }
  }
}
// H5调用方式
window.harmonyWebview.postMessage({method:'nativeMethod', data:'arg'})

更多关于HarmonyOS 鸿蒙NEXT实现H5 JSBridge方案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中实现H5与Native通信的JSBridge方案,核心思路与主流方案一致,主要通过Webview拦截自定义scheme实现双向通信。

  1. H5侧实现
  • 使用iframe发送jockey://scheme请求,通过window.postMessage实现消息传递
  • 维护listeners队列处理Native回调,callbacks队列处理异步响应
  • 典型API设计:Jockey.on()注册监听,Jockey.send()发起请求
  1. Native侧关键实现
// 拦截Webview请求
onLoadIntercept(event: { url: string }): boolean {
  if (event.url.startsWith('jockey://')) {
    this.handleJockeyRequest(event.url)
    return true // 拦截请求
  }
  return false
}

// 执行JS回调H5
sendToH5(type: string, data: string) {
  const js = \`window.Jockey.trigger("\${type}", \${data})\`
  this.webviewController.runJavaScript(js)
}
  1. 鸿蒙特有API
  • WebviewController提供runJavaScript方法执行H5脚本
  • onLoadIntercept回调拦截自定义scheme请求
  • 通过@ohos.web.webview模块实现Web能力

注意事项:

  1. 消息协议建议采用JSON格式统一数据格式
  2. 需要处理Webview的生命周期绑定问题
  3. 对于复杂数据类型需要序列化处理

这种方案在鸿蒙上的性能表现优于传统iframe方案,因鸿蒙对Webview有深度优化。实际开发时可结合具体业务需求扩展协议字段。

回到顶部