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