记录:离线包实现桥接

写在前面

  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 主动触发原生初始化
相关推荐
大舔牛3 分钟前
网站性能优化:小白友好版详解
前端·面试
转转技术团队12 分钟前
你的H5页面在折叠屏上适配了吗?
前端
北辰浮光24 分钟前
[Web数据控制]浏览器中cookie、localStorage和sessionStorage
前端·vue.js·typescript
Dolphin_海豚26 分钟前
charles proxying iphone
前端·ios
用户8417948145627 分钟前
vue 如何使用 vxe-table 来实现跨表拖拽,多表联动互相拖拽数据
前端·vue.js
x_y_35 分钟前
大家可以尝试一下前端AI CodeReview库
前端
moyu8443 分钟前
Vue3 作用域插槽:后台管理系统表格的灵动引擎
前端
好好好明天会更好44 分钟前
Vue中this.$options.data()是什么东西?
前端·vue.js