App与 JS 交互桥连接代码以及原理
概述: 1 使用demo 2 文档 3 源码解析
- 举个例子
1,js调用app
2,app 调用js
2,文档
(1)js 调用app
// 调用时传递的参数均为字符串,未方便书写,写成了对象形式,下同。
/**
* 向app发起请求
* actionName {String} 调用app的方法名
* @param args.__token {String} token为唯一标识,用来区别,同一方法的多次调用。
* @param args 其他的所有字段均为请求时需要的数据
*/
// js调用app,iOS
window.webkit.messageHandlers['actionName'].postMessage({
__token: 'token',
...args
});
// js调用app,Android
window.AndroidInterface['actionName']({
__token: 'token',
...args
});
// app处理完毕后回调js
/**
* app处理完毕后回调js
* @param args.__name {String} js调用app时使用的方法名
* @param args.__token {String} js调用时传递的token
* @param args.__status {Number} 是否处理成功,Number类型 0为成功,其他值为失败
* @param args.__message {String} 可选,如果处理失败,则必须在这个字段中添加失败描述
* @param args 其他的所有字段均为处理后的返回结果
*/
window['myApp2jsBridge.callback']({
__name: 'actionName',
__token: 'token',
__status: 0,
__message: '',
...args
});
- app 调用js
/**
* 向js发起请求
* @param args.__name {String} js处理完毕后回调app的方法名
* @param args.__token {String} token为唯一标识,用来区别,同一方法的多次调用。
* @param args 其他的所有字段均为请求时需要的数据
*/
window['myApp2jsBridge.trigger']({
__name: 'actionName',
__token: 'token',
...args
});
/**
* js处理完毕后回调app
* actionName {String} app发起调用时传递的方法名
* @param args.__token {String} app调用时传递的token
* @param args.__status {Number} 是否处理成功,Number类型 0为成功,其他值为失败
* @param args.__message {String} 可选,如果处理失败,则必须在这个字段中添加失败描述
* @param args 其他的所有字段均为处理后的返回结果
*/
// js回调app,iOS
window.webkit.messageHandlers['actionName'].postMessage({
__token: 'token',
__status: 0,
__message: '',
..args
});
// js回调app,Android
window.AndroidInterface['actionName']({
__token: 'token',
__status: 0,
__message: '',
...args
})
- 源码
-
/* eslint-disable */
/* 前端调用app后。告诉app 调用的方法,与此同时注册(同名)回调函数
* 在app执行完毕后,调用(同名)回调函数达到 get 参数的目的;
* 原理: 利用windows 为载体,发布订阅,结合 promise
*
*
*/
let CALLBACK_NAME = 'myApp2jsBridge.callback';
let TRIGGER_NAME = 'myApp2jsBridge.trigger';
const 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) {
args = args || {};
if (args.__status === 0) {
delete args.__name;
delete args.__token;
delete args.__status;
delete args.__message;
resolve(args);
} else {
reject(args.__message);
console.error(name + '\n' + 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);
},
);
});
}
/* eslint-disable */
/* 前端调用app后。告诉app 调用的方法,与此同时注册(同名)回调函数
* 在app执行完毕后,调用(同名)回调函数达到 get 参数的目的;
* 原理: 利用windows 为载体,发布订阅,结合 promise
*
*
*/
let CALLBACK_NAME = 'myApp2jsBridge.callback';
let TRIGGER_NAME = 'myApp2jsBridge.trigger';
const 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) {
args = args || {};
if (args.__status === 0) {
delete args.__name;
delete args.__token;
delete args.__status;
delete args.__message;
resolve(args);
} else {
reject(args.__message);
console.error(name + '\n' + 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);
},
);
});
}