混合开发
混合开发(Hybrid)是一种开发模式,指使用多种开发模型开发App,通常会涉及到两大类技术:原生 Native、Web H5
JSBridge 的概念和作用
-
通信桥梁:JSBridge 充当了 Web 应用和原生应用之间的通信桥梁。通过 JSBridge,我们可以在 web和原生代码之间进行双向通信,使这两者能够互相调用和传递数据。 原生功能调用:使用 JSBridge,我们可以在 JavaScript中调用原生应用中的功能。我们可以通过 web 来触发原生应用中的特定操作,如打开相机、发送通知、调用硬件设备等。
-
数据传递:JSBridge 使得 JavaScript 和原生代码之间可以方便地传递数据。意味着我们可以在 web 和原生代码之间传递复杂的数据结构,如对象、数组等,以满足应用的功能需求。
-
回调机制:JSBridge支持回调机制,使得在原生代码执行完某些操作后可以通知 JavaScript,并传递相应的结果。 JSBridge 做了什么
在Hybrid模式下,H5会需要使用Native的功能,比如打开二维码扫描、调用原生页面、获取用户信息等,同时Native也需要向Web端发送推送、更新状态等,而JavaScript是运行在单独的 JS Context 中(Webview容器)与原生有运行环境的隔离,所以需要有一种机制实现Native端和Web端的 双向通信 ,这就是JSBridge:以JavaScript引擎或Webview容器作为媒介,通过协定协议进行通信,实现Native端和Web端双向通信的一种机制。
/* eslint-disable */
/* eslint no-use-before-define: ["error", { "functions": false }] */
let CALLBACK_NAME = 'koolearnApp2jsBridge.callback';
let TRIGGER_NAME = 'koolearnApp2jsBridge.trigger';
let ua = navigator.userAgent.toLowerCase();
let isIOS = /iphone|ipad|ipod/.test(ua);
let isAndroid = /android/.test(ua);
let send = function(name, params) {
let data = JSON.stringify(params);
switch (true) {
case isIOS:
sendIOS(name, data);
break;
case isAndroid:
sendAndroid(name, data);
break;
default:
console.log('不是移动端', name, params);
}
};
let getToken = (function() {
let num = 0;
return function() {
num++;
return `${num}`;
};
})();
function clone(base, obj) {
if (arguments.length === 1) {
obj = base;
base = {};
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
base[key] = obj[key];
}
}
return base;
}
window[CALLBACK_NAME] = function(args) {
let name = args.__name;
let token = args.__token;
if (!name || !token) return;
if (window[CALLBACK_NAME][name] && window[CALLBACK_NAME][name][token]) {
window[CALLBACK_NAME][name][token](args);
delete window[CALLBACK_NAME][name][token];
}
};
// app主动触发js,把方法放到数组中
window[TRIGGER_NAME] = function(args) {
let name = args.__name;
let token = args.__token;
if (!name || !token) return;
if (window[TRIGGER_NAME][name]) {
window[TRIGGER_NAME][name].forEach((fn) => {
fn && fn(args);
});
}
};
function sendIOS(name, params) {
try {
window.webkit.messageHandlers[name].postMessage(params);
} catch (e) {
console.log('ios消息发送失败', e.message);
}
}
function sendAndroid(name, params) {
try {
params ? window.AndroidInterface[name](params) : window.AndroidInterface[name]();
} catch (e) {
console.log('android消息发送失败', e.message);
}
}
/**
* js向app发送详细
* @name<String>: 接口名
* @params<Object>:参数对象,不需要传空对象
* @needCallback<Boolean>: 是否需要Promise回调
* */
export function js2app(name, params, needCallback) {
if (!!needCallback === false) {
return send(name, params);
}
let token = getToken();
return new Promise((resolve, reject) => {
window[CALLBACK_NAME][name] = window[CALLBACK_NAME][name] || {};
window[CALLBACK_NAME][name][token] = function(args) {
if (name == 'checkPermission') {
console.log('js2app: ', args);
if (args.__status === 1) {
resolve(args);
} else {
reject(args);
}
return;
}
args = args || {};
if (args.__status === 0) {
delete args.__name;
delete args.__token;
delete args.__status;
delete args.__message;
resolve(args);
} else {
reject(args.__message);
}
};
send(name, clone({ __token: token }, params));
});
}
/**
* app调用js中的方法
* @name<String>: js中的方法名给app标识用
* @callback<Function>: js使用的回调方法,如果回调方法返回一个Primose,会自动通知app端
* */
export function app2js(name, callback) {
window[TRIGGER_NAME][name] = window[TRIGGER_NAME][name] || [];
window[TRIGGER_NAME][name].push((args) => {
let token = args.__token;
// var data = clone({}, args);
let promise;
delete args.__name;
delete args.__token;
promise = callback(clone({}, args));
promise && promise.then && promise.then(
(data) => {
let result = clone({
__token: token,
__status: 0,
}, data);
send(name, result);
},
(message) => {
let sendData = {};
let status = 1;
let result;
if (typeof message === 'object') {
sendData = message;
status = sendData.status || status;
message = sendData.message;
sendData = sendData.data || {};
}
result = clone({
__token: token,
__status: status,
__message: message,
}, sendData);
send(name, result);
},
);
});
}