@njy
2018-04-11T06:31:37.000000Z
字数 16388
阅读 1237
前端
新浪彩通
NativeAPI
NativeAPI 用于 JavaScript 与 Native Code 双向通信。
1.在 APP 中打开一个 WebView。
2.Native Code 向 WebView 添加全局的 NativeAPI 对象,此对象应当包含 sendToNative 方法,JavaScript 通过调用此方法向 Native 发送消息。
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
var send = function(message) {
window.console.log("javascript -> native: " + JSON.stringify(message));
if (isAndroid) {
// android
window.NativeAPI.sendToNative(JSON.stringify(message));
} else if (isiOS) {
// ios
window.webkit.messageHandlers.NativeAPI.postMessage(JSON.stringify(message))
}
};
exports.invoke = function(method, params, callback) {
var message = {
jsonrpc: "2.0",
method: method,
params: params
};
var id;
if (callback) {
id = "jsonp_" + idCounter;
idCounter++;
callbacks[id] = callback;
message.id = id;
}
send(message);
};
3.WebView 中的 JavaScript 在 NativeAPI 对象上注册 sendToJavaScript 方法,Native 通过调用此方法此方法向 JavaScript 发送消息。
window.NativeAPI.sendToJavaScript = function(message) {
window.console.log("native -> javascript: " + message);
try {
message = JSON.parse(message);
} catch (ex) {
return send({
jsonrpc: "2.0",
error: JSON_RPC_ERROR.PARSE_ERROR,
id: null
});
}
if (message.id) {
handleCallback(message);
}
};
语法:
--> 表示数据发送到服务器端
<-- 表示数据发送到客户端
位置参数形式的rpc调用:
rpc call with positional parameters:
--> {"jsonrpc":"2.0", "method": "subtract", "params":[42, 23], "id": 1}
<-- {"jsonrpc": "2.0", "result": 19,"id": 1}
var message = {
jsonrpc: "2.0",
method: "confirm",
params: { title: "", message: "", yes_btn_text: "", no_btn_text: ""},
id: "jsonp_1"
};
params 为 0、1、123 之类数字时,类型全部用 String(即使用 "0"、"1"、"123" 这样的字符串)。
使用 Scheme URL 注入 Native 部分
if ( /hbgj/i.test(appName) ) {
window.location.href = "openetjs://start?type=nativeapi";
} else if ( /gtgj/i.test(appName) ) {
window.location.href = "gtgj://start?type=nativeapi";
}
confirm
title
String 标题信息message
String 对话框消息yes_btn_text
String 确定按钮⽂文字no_btn_text
String 取消按钮⽂文字YES
Number 常量 1NO
Number 常量 0CLOSE
Number 常量 2value
Number 0/1/2JS -> Native: {method: "confirm", params: { title: "test", message: "hello world", yes_btn_text: "是的", no_btn_text: "不是" }, id: 1}
Native -> JS: {result {value: 0, YES:1, NO: 0, CLOSE: 2}, id: 1}
// invoke 方法是对 sendToNative 的封装
NativeAPI.invoke(
"confirm", {
title: "提示",
message: "message",
yes_btn_text: "~确定~",
no_btn_text: "~取消~"
},
function(err, data) {
if (err) {
return handleError(err);
}
switch (data.value) {
case data.YES:
echo("你点了确定按钮");
break;
case data.NO:
echo("你点了取消按钮");
break;
case data.CLOSE:
echo("你使用其他方式关闭了弹窗");
break;
default:
echo("未知动作,返回code是[" + data.value + "]");
}
}
);
alert
title
String 标题信息message
String 对话框消息btn_text
String 确定按钮⽂文字YES
Number 常量 1CLOSE
Number 常量 0value
Number 1 或者 0
NativeAPI.invoke(
"alert", {
title: "这是标题",
message: "这是消息",
btn_text: "确定按钮"
},
function(err, data) {
if (err) {
return handleError(err);
}
switch (data.value) {
case data.YES:
echo("你点了确定按钮");
break;
case data.CLOSE:
echo("你使用其他方式关闭了弹窗");
break;
default:
echo("未知动作,返回code是[" + data.value + "]");
}
}
);
close
closeAll
createWebView
url
String url有可能是相对的,如果是相对路径的话就根据当前 webView 的 url 进⾏计算controls
Array controls是一个数组,元素中 type: title 表示要设置的是标题,text: "Native Page 2" 为具体文本。
NativeAPI.invoke("createWebView", {
url: window.location.origin + "xxx",
controls: [
{
type: "title",
text: "Native Page 2"
}
]
});
back
webViewCallback
url
String url有可能是相对的,如果是相对路径的话就根据当前webView的url进⾏计算 login
SUCC
常量,表示登录流程完成 FAIL
常量,表示登录流程中断 CANCEL
常量,表示用户在登录过程中作了取消操作 value
它的值应该是 SUCC/FAIL/CANCEL 其中之一,用来判断成功/失败等状态。getUserInfo
appName
String "gtgj"代表高铁管家,"hbgj"代表航班管家。phone
String ⽤户电话uid
Stringuserid
String ⽤户IDauthcode
Stringhbuserid
String 过渡期参数,下一版本统一用 userid。hbauthcode
String 过渡期参数,下一版本统一用 authcode。code
getDeviceInfo
imei
String 移动设备国际识别码p
String 用于识别 APP 的自定义的标识uuid
String 该字段包含⽤户的登录信息,后端 API 可以通过将此字段兑换成 sessionId 之类的数据用于校验⽤户登录状态。channel
String 例如:appstorename
String 例如:gtgjversion
String APP 版本makePhoneCall
number
String 电话号码valu
Number updateTitle
text
String 标题栏⽂文字storage
action
String 操作名称 [get|set|remove]key
Stringvalue
Stringvalue
StringupdateHeaderRightBtn
type
String 指定按钮类型,例如:"phone", "order", "share"等。action
String 控制按钮显⽰与隐藏,当 action == "show" 的时候展⽰,当 action == "hide" 的时候隐藏。icon
String 按钮图片,图片的base64编码,如果没有找到icon则显⽰text字段,text字段默认为空。text
String 按钮文字,没有 icon 字段时使用。 isSupported
method
String 需要查询的方法value
Boolean true 支持,false 不支持。由于历史原因,isSupported 方法目前返回value:"1/0"。selectContact
selectedContacts
String phone以逗号分隔。表示已经选择的联系人,用于显示联系人列表时自动选中。maxNum
String 表示选择联系人的数量上限,默认为1(即单选),0 则表示不设上限。name
String 联系人的名字phone
Array 电话号码数组contacts
Array 以数组形式返回联系人信息。原本的 name
和 phone
字段,则为数组中第一个联系人的信息(即兼容单选)。setOrientation
orientation
String 默认值为 portrait
(强制竖屏),其他值包括 auto
(跟随手机旋转),landscape
(强制横屏)。setGestureBack
preventDefault
Boolean false 表⽰执⾏默认⾏为(手势返回有效),true 表⽰阻⽌止默认⾏为(手势返回无效)。sendSMS
phone
Stringmessage
Stringvalue
Boolean true 成功,false 不成功。getCurrentPosition
latitude
The latitude as a decimal numberlongitude
The longitude as a decimal numberaccuracy
The accuracy of positionheading
The heading as degrees clockwise from Northspeed
The speed in meters per secondtimestamp
The date/time of the responsescanBarcode
value
String 扫描得到的字符串sharePage
title
String 分享块标题desc
String 分享块描述link
String 分享块链接imgUrl
String 分享块图片type
String 分享类型;支持 weixin
weibo
pengyouquan
sms
email
;如果是 all
表示想要分享到所有,此参数为空或不传此参数时,效果应等同于 all
。value
Boolean true 成功,false 不成功。trackEvent
event
String 记录事件名attrs
String 统计字符串(JSON String)startPay
quitpaymsg
String 退出时候的提示title
String 支付标题price
String 商品价格orderid
String 订单号productdesc
String 商品描述 url
String 显示订单基本信息的Wap页面 subdesc
String 商品详情描述 canceltype
String 取消等待支付结果的跳转类型:0代表跳转机票订单,1代表跳转酒店订单,2代表跳转其他订单,-1代表不做跳转(默认为-1) SUCC
常量,表示支付成功FAIL
常量,表示支付失败CANCEL
常量,表示用户取消了支付(高铁管家3.3开始支持)PENDING
常量,表示目前为支付待确认状态,由于第三方支付结果可能需要等待较长的时间,未知状态时可以返回 PENDINGvalue
SUCC/FAIL/CANCEL/PENDING
NativeAPI.invoke("startPay", {
quitpaymsg: "您尚未完成支付,如现在退出,可稍后进入“个人中心->其他订单”完成支付。确认退出吗?, ",
title: "商城订单",
price: "145",
orderid: "150513188189022",
productdesc: "接机订单支付",
subdesc: "专车接送",
url: "http://jp.rsscc.com/Fmall/rest/client/v4/pay.htm?orderid=150513188189022",
canceltype: -1
}, function(err, data) {
switch (data.value) {
case data.SUCC:
alert("支付成功, 跳转详情");
break;
case data.FAIL:
alert("支付失败");
break;
case data.CANCEL:
alert("您取消了支付");
break;
case data.PENDING:
alert("目前为支付待确认状态");
break;
default:
alert("支付异常");
}
});
loading
show
String 1 显示,0 隐藏text
String 显示的文字SUCC
常量,正常显示FAIL
常量,显示失败CANCEL
常量,点了返回键value
SUCC/FAIL/CANCELresume
prevUrl
String 当前 webview 为基准,前一个 webview 的 URL(没有就为空)。nextUrl
String 当前 webview 为基准,后一个 webview 的 URL。 back
preventDefault
Boolean false 表⽰执⾏默认⾏为(在 Android 上表⽰⽴刻关闭当前界⾯),true 表⽰阻⽌止默认⾏为。 Native -> JS: {method: "back", params:null, id: 1}
JS -> Native: {result: {preventDefault: false}, id: 1}
// 先调用 updateHeaderRightBtn 设置右边按钮的文字或图片,再调用 headerRightBtnClick 设置点击了右侧按钮后做什么。
rightButtonText: function() {
var self = this;
NativeAPI.invoke("updateHeaderRightBtn", {
action: "show",
text: "分享",
data: {
list: [1, 2, 3]
}
}, function(err, data) {
if (err) {
return handleError(err);
}
echo(JSON.stringify(data));
});
NativeAPI.registerHandler("headerRightBtnClick", function(data) {
echo(JSON.stringify(data));
self.share();
});
}
var _nativeAPI = {};
(function(exports) {
var JSON_RPC_ERROR = {
PARSE_ERROR: {
code: -32700,
message: "Parse error"
},
INVALID_REQUEST: {
code: -32600,
message: "Invalid Request"
},
METHOD_NOT_FOUND: {
code: -32601,
message: "Method not found"
},
INVALID_PARAMS: {
code: -32602,
message: "Invalid params"
},
INTERNAL_ERROR: {
code: -32603,
message: "Internal error"
}
};
var methods = {};
var callbacks = {};
var idCounter = 1;
var send = function(message) {
window.console.log("javascript -> native: " + JSON.stringify(message));
window.NativeAPI.sendToNative(JSON.stringify(message));
};
var handleCallback = function(message) {
var callback = callbacks[message.id];
callbacks[message.id] = null;
if (!callback) {
return;
}
setTimeout(function() {
callback(message.error || null, message.result);
}, 0);
};
var handleInternalError = function(message) {
try {
message = JSON.parse(message);
} catch (ex) {
return;
}
if (message.id) {
handleCallback({
jsonrpc: "2.0",
error: JSON_RPC_ERROR.INTERNAL_ERROR,
id: message.id
});
}
};
window.NativeAPI = window.NativeAPI || {};
// NativeAPI 会在 document.onload 的事件中被注册,而该事件会一直等到页面上图片加载结束之后才触发,所以 NativeAPI 速度非常慢。当发现身处 native 中的时候,将图片加载放在 NativeAPI 加载完毕之后进行。 客户端反馈iOS页面加载前会注入成功,android会在页面1/4左右加载完成
if (!window.NativeAPI.sendToNative) {
(function() {
var buffer = [];
var timer = setTimeout(function() {
buffer.forEach(handleInternalError);
window.NativeAPI.sendToNative = handleInternalError;
}, 3000);
document.addEventListener("WebViewJavascriptBridgeReady", function() {
clearTimeout(timer);
setTimeout(function() {
// 注入的NativeAPI会覆盖前端定义的NativeAPI
buffer.forEach(window.NativeAPI.sendToNative);
}, 10);
}, false);
window.NativeAPI.sendToNative = function(message) {
buffer.push(message);
};
try{
// 加速 NativeAPI 注册
// var appName = util.cookie.getItem('appName');
// if (appName === 'hbgj') {
// window.location.href = 'openetjs://start?type=nativeapi';
// } else if (appName === 'gtgj') {
// window.location.href = 'gtgj://start?type=nativeapi';
// }
}catch(e){}
})();
}
window.NativeAPI.sendToJavaScript = function(message) {
window.console.log("native -> javascript: " + message);
try {
message = JSON.parse(message);
} catch (ex) {
return send({
jsonrpc: "2.0",
error: JSON_RPC_ERROR.PARSE_ERROR,
id: null
});
}
if (message.id) {
handleCallback(message);
}
};
exports.invoke = function(method, params, callback) {
var message = {
jsonrpc: "2.0",
method: method,
params: params
};
var id;
if (callback) {
id = "jsonp_" + idCounter;
idCounter++;
callbacks[id] = callback;
message.id = id;
}
send(message);
};
})(_nativeAPI);
_nativeAPI.invoke('confirm', confirmData, function(err, data) {
if (data.value === data.YES) {
...
} else {
...
}
});
_nativeAPI.invoke('alert', alertData, function(err, data) {
...
});
_nativeAPI.invoke('close');
_nativeAPI.invoke('createWebView', {url: mapUrl});
_nativeAPI.invoke('login', null, function(err, data) {
if (err === null && (data.succ == '1' || data.value === data.SUCC)) {
...
}
});
_nativeAPI.invoke('getUserInfo', {}, function(err, data) {
// 管家自身已经登录
if (err == null && data.jwt) {
self.user = data;
} else {
// 没有登录,登录检测到此结束
self.isLogin(false);
}
});
/*
* 因为安卓获取 userinfo 是异步的,导致登录之后 userInfo.userid 不一定能获取到,所以加个循环
*/
var timeout = null;
function getUserInfo() {
_nativeAPI.invoke('getUserInfo', {}, function(err, data) {
// alert(JSON.stringify(data));
// iOS 3.2 全部返回了
if (err == null && data.jwt) {
self.user = data;
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
} else {
timeout = setTimeout(getUserInfo, 500);
}
});
}
getUserInfo();
_nativeAPI.invoke('getDeviceInfo', null, function(err, data) {
if (err === null) {
...
} else {
...
}
});
_nativeAPI.invoke('updateTitle', {text: title});
_nativeAPI.invoke('storage', {
action: 'set',
key: 'lastHotel',
value: JSON.stringify(data.info)
});
_nativeAPI.invoke('getCurrentPosition', null, function(err, result) {
if (!result.errDesc) {
try {
util.currentPosition = result;
var lat = util.currentPosition.latitude;
var lng = util.currentPosition.longitude;
if(lat && (lat > 90) ){
util.currentPosition.latitude = lng;
util.currentPosition.longitude = lat;
}
self.getAddressByCoor(currentPlatName);
} catch(e) {
util.currentPosition = null;
util.alert({
title: '获取位置失败',
content: '请在系统设置中打开“定位服务”,并允许“'+currentPlatName+'”获取您的位置'
});
}
} else{
util.currentPosition = null;
util.alert({
title: '获取位置失败',
content: '请在系统设置中打开“定位服务”,并允许“'+currentPlatName+'”获取您的位置'
});
}
});
_nativeAPI.invoke('scanBarcode', {}, function(err, value) {
// value扫描得到的字符串
})
_nativeAPI.invoke('sharePage', {
title: '分享',
desc: '',
link: '',
imgUrl: '',
type: 'all'
}, function(err, value) {
if (err === null && value) {
...
} else {
...
}
})
_nativeAPI.invoke('startPay', {
title: '支付 - ' + self.name(),
price: data.orderInfo.payPrice,
orderid: data.orderInfo.orderId
}, function(err, nativeData) {
if (nativeData.value === nativeData.SUCC) {
...
} else if(nativeData.value === nativeData.FAIL) {
/*
* 特殊说明: 支付模块会一直监听支付结果,直到支付成功;否则就是用户 cancel 了。不存在 FAIL 这个
*/
...
} else if (nativeData.value === nativeData.CANCEL) {
...
} else {
...
}
});
_nativeAPI.invoke('loading', {show: true, text: '加载中'}, function() {
...
});
;(function(win, doc) {
function run() {
// 业务代码
...
}
util.PLATFORM = {
BROWSER: 1,
WEIXIN: 2,
XIAOPAO: 3,
CURRENT: 1,
CURRENT_STR: '普通浏览器'
};
/*
* 『微信支付』的界面, url 没有 from 参数的情况,从而导致判断失败。将当前 PLATFORM 记在 cookie 中,根据 cookie 来判断是否在 native 中。 该 cookie 名是__platform
* NativeAPI 会在 document.onload 的事件中被注册,而该事件会一直等到页面上图片加载结束之后才触发,所以 NativeAPI 速度非常慢。当发现身处 native 中的时候,将图片加载放在 NativeAPI 加载完毕之后进行。
*/
if (win.location.href.indexOf('from=xiaopao') >= 0) {
util.PLATFORM.CURRENT = util.PLATFORM.XIAOPAO;
util.PLATFORM.CURRENT_STR = '小炮';
} else if (win.navigator.userAgent.toLowerCase().indexOf('micromessenger') >= 0) {
util.PLATFORM.CURRENT = util.PLATFORM.WEIXIN;
util.PLATFORM.CURRENT_STR = '微信';
} else {
if (util.cookie.getItem('__platform')) {
util.PLATFORM.CURRENT = win.parseInt(util.cookie.getItem('__platform'));
if (util.PLATFORM.CURRENT == util.PLATFORM.WEIXIN) {
util.PLATFORM.CURRENT_STR = '微信';
} else if (util.PLATFORM.CURRENT == util.PLATFORM.XIAOPAO) {
util.PLATFORM.CURRENT_STR = '小炮';
}
}
}
util.cookie.setItem('__platform', util.PLATFORM.CURRENT);
switch(util.PLATFORM.CURRENT) {
case util.PLATFORM.BROWSER:
run();
break;
case util.PLATFORM.WEIXIN:
if (util.cookie.getItem('wx_open_id')) {
m.loadScript('http://res.wx.qq.com/open/js/jweixin-1.0.0.js', function() {
run();
});
} else {
m.loadScript('http://res.wx.qq.com/open/js/jweixin-1.0.0.js', function() {
run();
});
// 需要微信网页授权
// window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxa2f3f0d342b39a3a&redirect_uri='
// + window.apiRootPath
// + '&response_type=code&scope=snsapi_base#wechat_redirect';
}
break;
case util.PLATFORM.XIAOPAO:
m.loadScript(__uri('lib/native-api.js'), function() {
// util.speedUpNativeAPI(); (window.location.href = "openetjs://start?type=nativeapi";)
_nativeAPI.invoke('getDeviceInfo', null, function(err, result) {
try {
// util.COMMON_PARAMS 对象存储APP提交给PHP所有的公共参数
util.COMMON_PARAMS.comm_imei = result.imei;
} catch(e) {
util.COMMON_PARAMS.comm_imei = 'unknown';
}
run();
});
});
break;
}
})(window, document);