记录:离线包实现桥接

写在前面

  1. 使用 WebViewJavascriptBridge 框架,将WebViewJavascriptBridge对象由原生端注入到WebView中。
  2. 前端检验 window.WebViewJavascriptBridge 是否存在
  3. 通过特定机制(事件监听/iframe) 等待桥接准备就绪
  4. 使用原生注入的 WebViewJavascriptBridge 对象提供的方法:
    • callHandler: 调用原生功能
    • registerHandler: 注册供原生调用的方法

jsBridge编写

新建一个jsBridge文件

js 复制代码
const u = navigator.userAgent
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1

/* eslint-disable */
function setupWebViewJavascriptBridge(callback) {
  // 第一次调用这个方法的时候,为false
  if (window.WebViewJavascriptBridge) {
    return callback(WebViewJavascriptBridge)
  }
  if (isAndroid) {
    document.addEventListener('WebViewJavascriptBridgeReady', function () {
      callback(WebViewJavascriptBridge)
    }, false)
  } else {
    // 第一次调用的时候,也是false
    if (window.WVJBCallbacks) {
      return window.WVJBCallbacks.push(callback)
    }
    // 把callback对象赋值给对象。
    window.WVJBCallbacks = [callback]
    // 这段代码的意思就是执行加载WebViewJavascriptBridge_JS.js中代码的作用
    var WVJBIframe = document.createElement('iframe')
    WVJBIframe.style.display = 'none'
    WVJBIframe.src = 'https://__bridge_loaded__'
    document.documentElement.appendChild(WVJBIframe)
    setTimeout(function () {
      document.documentElement.removeChild(WVJBIframe)
    }, 0)
  }
}

/* Android 版本 WebViewJavascriptBridge 的设计要求:
通过调用 init() 方法,建立 JS 端与 Android 原生端的双向通信通道

iOS 版本的 WebViewJavascriptBridge 在桥接对象注入时自动完成初始化,
通过 iframe 机制触发加载后,桥接自动就绪 
*/
if (isAndroid) {
  setupWebViewJavascriptBridge(function (bridge) {
    bridge.init()
  })
}
export default {
  callhandler(name, data, callback) {
    setupWebViewJavascriptBridge(function (bridge) {
      bridge.callHandler(name, data, callback)
    })
  },
  registerhandler(name, callback) {
    setupWebViewJavascriptBridge(function (bridge) {
      bridge.registerHandler(name, function (data, responseCallback) {
        callback(data, responseCallback)
      })
    })
  }
}

Android

Android使用 addEventListener 的原因:

  • Android WebView 提供了标准的事件机制
  • 原生端可以在适当时候触发 JavaScript 事件
  • 通过 document.addEventListener 监听自定义事件是一种标准做法

Android原生端在初始化完成后主动触发 WebViewJavascriptBridgeReady 事件通知 JS 端

iOS

iOS 使用 iframe 的原因:

  • UIWebView(较老的 iOS WebView)没有像 Android 那样的直接事件触发机制
  • 但 WebView 会拦截特定 URL 请求,这是可以利用的特性

使用URL Scheme 拦截机制:

js 复制代码
var WVJBIframe = document.createElement('iframe') 
WVJBIframe.style.display = 'none' 
WVJBIframe.src = 'https://__bridge_loaded__' 
document.documentElement.appendChild(WVJBIframe)

创建一个隐藏的 iframe 并设置特殊 URL,iOS 原生端通过拦截这个 URL 请求来知道需要初始化桥接。

使用

index.js

js 复制代码
  import jsBridge from './jsBridge'
  
  /**
   * methods downloadImage  下载图片
   * params list {Array} base64列表parmas = {list: [base64]} base64不要前面的信息 img/png
   *        title {string}
   * */
    downloadImage  (params, responseCallback) {
        jsBridge.callhandler('downloadImage', params, (data) => {
          responseCallback(JSON.parse(data))
        })
    },
  
    /**
    * methods checkSmsCode 校验验证码
    * params businessCode  {string} (modifyDrawPasswordDynamic     銀行卡修改取款密碼
    * params strategyCode {string} (ebil_ebilling写死)
    * */
    checkSmsCode (params, responseCallback) {
        jsBridge.callhandler('checkSmsCode', params, (data) => {
          let obj = JSON.parse(data)
          if (obj.authStatus === 'pass') {
            responseCallback(JSON.parse(data))
          } else {
            Toast.info('驗證碼錯誤_SMS code Error')
          }
        })
   },
   
  /**
   * methods 校验账户的交易密码
   * params {} 无需传参
   * */
  getCheckPassword (responseCallback) {
    jsBridge.callhandler('getCheckPassword', {}, data => {
      if (data) {
        responseCallback(JSON.parse(data))
      }
    })
  },

前端页面:

js 复制代码
    getImg () {
      this.$bridge.downloadImage({ list: this.url }, () => {
        // console.log(res)
      })
    },
    nextPage () {
      this.$bridge.checkSmsCode({ businessCode: 'creditAutoAply', strategyCode: 'credit_aply_sms_code' }, res => {
        this.$bridge.getCheckPassword(data => {
          if (data.authStatus) {
            // 保存文件信息
            this.$remote(SAVE_FILE_INFO, params).then(res => {
              ....
            })
          }
        })
      })
    }

总结

  • Android: 利用标准事件机制,原生主动通知 Web
  • iOS: 利用 URL 拦截机制,Web 主动触发原生初始化
相关推荐
mCell5 小时前
GSAP ScrollTrigger 详解
前端·javascript·动效
gnip5 小时前
Node.js 子进程:child_process
前端·javascript
excel8 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
excel9 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼10 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping10 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙11 小时前
[译] Composition in CSS
前端·css
白水清风11 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix12 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts
用户221520442780012 小时前
new、原型和原型链浅析
前端·javascript