h5与原生的交互方式

580 阅读3分钟

一、将方法注入的window对象上

app将方法注入的window对象上,h5通过window拿到app注入的方法 调用该方法

同样app想访问h5的方法,h5将方法挂到window对象上,app通过window拿到该方法

if (window.appInfo) {
    window.appInfo.invoke(参数) // 调用app注入的appInfo对象上的invoke方法,将参数传给app
}

二、拦截URL SCHEME

  1. 通过localtion.href;

通过location.href有个问题,就是如果我们连续多次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。

  1. 通过iframe方式;

URL SCHEME:URL SCHEME是一种类似于url的链接,是为了方便app直接互相调用设计的,形式和普通的 url 近似,主要区别是 protocol 和 host 一般是自定义的,例如: qunarhy://hy/url?url=ymfe.tech,protocol 是 qunarhy,host 则是 hy。

在时间过程中,这种方式有一定的 缺陷:

  • 使用 iframe.src 发送 URL SCHEME 会有 url 长度的隐患。
  • 创建请求,需要一定的耗时,比注入 API 的方式调用同样的功能,耗时会较长。

三、postMessage

postMessage可以安全地实现跨源通信。从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.opener),然后在窗口上调用 targetWindow.postMessage() 方法分发一个消息。 对于 iOS 的 UIWebView,前端调用方式:

window.postBridgeMessage(message);

对于 iOS 的 WKWebView,前端调用方式:

window.webkit.messageHandlers.xxx(app声明的方法).postMessage(message);

对于 Android 的 WKWebView,前端调用方式:

window.nativeBridge.postMessage(message);

通信原理之先了解webview

IOS容器 在IOS客户端中,我们首先要提到的是一个叫UIWebView的容器,苹果对他的介绍是:

UIWebView是一个可加载网页的对象,它有浏览记录功能,且对加载的网页内容是可编程的。说白了UIWebView有类似浏览器的功能,我们使用可以它来打开页面,并做一些定制化的功能,如可以让js调某个方法可以取到手机的GPS信息。

但需要注意的是,Safari浏览器使用的浏览器控件和UIwebView组件并不是同一个,两者在性能上有很大的差距。幸运的是,苹果发布iOS8的时候,新增了一个WKWebView组件容器,如果你的APP只考虑支持iOS8及以上版本,那么你就可以使用这个新的浏览器控件了。

WKWebView重构了原有UIWebView的14个类,3个协议,性能提升的同时,赋予了开发者更加细致的配置(这些配置仅针对客户端IOS开发,对于前端H5来说,保持两种容器调用方法的一致性很重要)。

Android容器 在安卓客户端中,webView容器与手机自带的浏览器内核一致,多为android-chrome。不存在兼容性和性能问题。

RN容器 在react-native开发中,从rn 0.37版本开始官方引入了组件,在安卓中调用原生浏览器,在IOS中默认调用的是UIWebView容器。从IOS12开始,苹果正式弃用UIWebView,统一采用WKWebView。

RN从0.57起,可指定使用WKWebView作为WebView的实现

brdge.js


export const setupWebViewJavascriptBridge = callback => {
  if (env === 'android') {
      document.addEventListener(
        'WebViewJavascriptBridgeReady',
        function () {
          callback(window.WebViewJavascriptBridge)
        },false)
    } else if (env === 'ios') {
      if (window.WKWebViewJavascriptBridge) {
        return callback(WKWebViewJavascriptBridge)
      }
      if (window.WKWVJBCallbacks) {
        return window.WKWVJBCallbacks.push(callback)
      }
      window.WKWVJBCallbacks = [callback]
      // 方法1、postMessage
      window.webkit.messageHandlers.IOS_Native_FUNCT.postMessage(null) 
      // 方法2、iframe
      let WVJBIframe = document.createElement('iframe')

      WVJBIframe.style.display = 'none'
      WVJBIframe.src = 'wvjbscheme://_BRI_'
      document.documentElement.appendChild(WVJBIframe)
      setTimeout(() => {
          document.documentElement.removeChild(WVJBIframe)
      }, 0)
}

总结一下过程

  • JS判断是否存在window.WebViewJavascriptBridge对象
  • 如果有的话就进行通信
  • 如果没有的话就需要,将所有JSBridge函数调用放到window.WKWVJBCallbacks数组种,android中当WebViewJavascriptBridgeReady事件触发后,此任务队列将会被刷新;ios端当拦截到url或者posmessage时 此任务队列也会执行
  • WebViewJavascriptBridge 是用于在WKWebView,UIWebView和WebView中的Obj-c和js之间发送消息的IOS/osx桥接器

上面选择用iframe 是因为Native层只能接收到最后一次请求

image.png