Hybrid App混合开发通信

混合app即外壳为原生开发,安卓或者ios。业务为H5的APP。这样的APP可以减少开发成本。也可以热更新,用户不用更新app即能使用新功能。
其中混合开发涉及一个通信的问题。即业务系统需要使用一个app功能时,需要H5调用native的功能。native完成一些操作也需要通知到业务系统,即native需要调用H5。
通信原理:
1、native通过注入方法到window对象上让js调用,实现H5调用native
2、native通过调用window上的方法调用H5

一、h5调用iOS、Android

1. h5 -> iOS
h5通过调用window.webkit.messageHandlers..postMessage()来与iOS通信
(注1:其中name表示ios这边注入的js方法,目前驻场app用的name=h5CallNative)

(注2:其中body表示js要传递给iOS的数据结构,目前传递的是对象,格式见下方,参数说明见1.3)

body = {“type”:“xxx”, “subType”:“x”, “params”:{“key1”:“value1”}, “callback”:“cccc”}

2. h5 -> Android

h5通过调用window.jsObject.h5CallNative()
(注1:Android APP 会注入jsObject对象以及h5CallNative方法) (注2:其中body是json字符串)

3. 参数说明
对于body中,几个参数的解释

type - (必要参数 不能为空) 用来区分具体要做的功能,例如打电话,分享,唤起native页面(例如唤起登陆页面、充值页面),目前支持的列表见最下方
subType - (参数 可为空) 对type的补充 具体见最下方
params - (参数 可为空) h5传给native的参数
callback - (参数 可为空) navite回调h5的方法

二、iOS、Android调用h5

ios或者安卓直接调用window上的方法即可nativeCallH5

下面是vue项目封装native.js实现jsbridge的作用。


import Vue from 'vue'
import doStrategi from './strategy.js'
window.nativeCallH5 = function (body) {
  /* if (!window.jsObject) {
    return
  } */
  if (body.type === 'nfc' && body.subType === 'saveInfo') {
    doStrategi('addRosterUser', body.params)
  }
}

Vue.prototype.$jsBridge = (function () {
  function CallApp1 (ops) {
    var randomStr = String(Math.random()).substring(2, 5)
    this.config = {
      istimeout: ops.istimeout == undefined && true || ops.istimeout,
      timerId: null, // 定时器ID
      isDelet: true,
      timeout: 3000 // 调用app方法超时时间
    }
    this.appData = {
      type: 'lbs',
      subType: '',
      params: {},
      callback: 'callYlwH5' // native回调h5统一方法
    }
    this.ops = ops
    this.setting = {...this.config, ...ops}
    if (this.setting.noRandom) {
      this.setting.callback = this.setting.callback && this.setting.callback || 'callYlwH5'
    } else {
      this.setting.callback = this.setting.callback && this.setting.callback + randomStr || 'callYlwH5' + randomStr
    }
    this.init()
  }
  CallApp1.prototype.init = function () {
    this.invokeCallback()
    var _appData = this.getAppData(this.setting)
    // this.alert(_appData)
    // ios
    /* if (isIOS() && (navigator.userAgent.indexOf('Safari') == -1)) {
      window.webkit.messageHandlers.JsToNativeMethod.postMessage(_appData);
    } */
    // 安卓
    if (window.jsObject) {
      window.jsObject.h5CallNative(JSON.stringify(_appData))
    }
  }
  CallApp1.prototype.invokeCallback = function () {
    var self = this
    window[self.setting.callback] = function () {
      self.success.apply(self, arguments)
    }
    this.toggleTimeout.call(self, 'open')
  }
  CallApp1.prototype.toggleTimeout = function (type) {
    var self = this
    if (!self.config.istimeout) return false
    if (type === 'close') {
      // 如果定时器ID存在则清除
      if (self.config.timerId) {
        clearTimeout(self.config.timerId)
        self.config.timerId = null
      }
    } else if (type === 'open') {
      // 超时的话调用fail方法
      self.config.timerId = setTimeout(function () {
        self.fail.apply(self, arguments)
      }, self.config.timeout)
    }
  }
  CallApp1.prototype.getAppData = function (ops) {
    ops = ops || {}
    var self = this
    var _appData = {}
    for (var key in self.appData) {
      if (ops.hasOwnProperty(key)) {
        _appData[key] = ops[key]
      } else {
        _appData[key] = self.appData[key]
      }
    }
    return _appData
  }
  CallApp1.prototype.str2JSON = function (data) {
    return typeof data === 'string' ? JSON.parse(data) : data
  }
  CallApp1.prototype.fail = function (data) {
    data = this.str2JSON(data)
    if (typeof this.setting.fail === 'function') {
      this.setting.fail.call(this, data)
    }
    this.complete(data)
  }
  CallApp1.prototype.success = function (data) {
    data = this.str2JSON(data)
    if (typeof this.setting.success === 'function') {
      this.setting.success.call(this, data)
    }
    this.complete(data)
  }
  CallApp1.prototype.complete = function (data) {
    if (typeof this.setting.complete === 'function') {
      this.setting.complete.call(this, data)
    }
    this.toggleTimeout.call(this, 'close')
    // 删除回调方法
    if (this.setting.isDelet) {
      delete window[this.setting.callback]
    }
  }

  return {
    callApp (ops) {
      return new CallApp1(ops)
    },
  /**
   * 设置nfc
   */
    setNfc (subType, callback) {
      let body = {
        'type': 'nfc',
        'subType': subType,
        'params': {},
        success: callback
      }
      this.callApp(body)
    },
    /**
     *
     * @param {*} callback
     */
    toSetNfc (callback) {
      let body = {
        'type': 'settings',
        'subType': 'NFCsettings',
        'params': {},
        success: callback
      }
      this.callApp(body)
    },
    /**
     * 查询nfc设置
     */
    initNfcCfg (callback) {
      let body = {
        'type': 'nfc',
        'subType': 'isEnabled',
        'params': {},
        success: callback
      }
      this.callApp(body)
    },
    clearHistory () {
      let body = {
        'type': 'browser',
        'subType': 'clearHistory',
        'params': {}
      }
      this.callApp(body)
    },
    /**
     *
     * @param {*} res
     */
    loginApp (res) {
      let body = {
        'type': 'account',
        'subType': 'login',
        'params': res,
        success: function () {

        }
      }
      this.callApp(body)
    },
    logoutApp () {
      let body = {
        'type': 'account', // 用来区分具体要做的功能
        'subType': 'logout', // 对type的补充
        'params': {}, // native传给h5的参数(可为空)
        success: function (res) {

        }
      }
      this.callApp(body)
    },
    /**
     *
     */
    broadcast (text) {
      let body = {
        'type': 'speech', // 用来区分具体要做的功能
        'subType': '', // 对type的补充
        'params': {
          'text': text, 'voiceName': 'xiaoyan', 'speed': '50', 'pitch': '50', 'volume': '50'
        }, // native传给h5的参数(可为空)
        success: function (res) {

        }
      }
      this.callApp(body)
    }
  }
})()


然后在组件中这样调用即可

this.$jsBridge.loginApp(res)

你可能感兴趣的:(vue,混合开发,hybrid开发,H5与native通信)