@njy
2018-04-11T06:31:37.000000Z
字数 16388
阅读 1285
前端 新浪彩通 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) {// androidwindow.NativeAPI.sendToNative(JSON.stringify(message));} else if (isiOS) {// ioswindow.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";}
confirmtitle 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 + "]");}});
alerttitle 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 + "]");}});
closecloseAllcreateWebViewurl 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 webViewCallbackurl String url有可能是相对的,如果是相对路径的话就根据当前webView的url进⾏计算 loginSUCC 常量,表示登录流程完成 FAIL 常量,表示登录流程中断 CANCEL 常量,表示用户在登录过程中作了取消操作 value 它的值应该是 SUCC/FAIL/CANCEL 其中之一,用来判断成功/失败等状态。getUserInfoappName String "gtgj"代表高铁管家,"hbgj"代表航班管家。phone String ⽤户电话uid Stringuserid String ⽤户IDauthcode Stringhbuserid String 过渡期参数,下一版本统一用 userid。hbauthcode String 过渡期参数,下一版本统一用 authcode。code getDeviceInfoimei String 移动设备国际识别码p String 用于识别 APP 的自定义的标识uuid String 该字段包含⽤户的登录信息,后端 API 可以通过将此字段兑换成 sessionId 之类的数据用于校验⽤户登录状态。channel String 例如:appstorename String 例如:gtgjversion String APP 版本makePhoneCallnumber String 电话号码valu Number updateTitletext String 标题栏⽂文字storageaction String 操作名称 [get|set|remove]key Stringvalue Stringvalue StringupdateHeaderRightBtntype String 指定按钮类型,例如:"phone", "order", "share"等。action String 控制按钮显⽰与隐藏,当 action == "show" 的时候展⽰,当 action == "hide" 的时候隐藏。icon String 按钮图片,图片的base64编码,如果没有找到icon则显⽰text字段,text字段默认为空。text String 按钮文字,没有 icon 字段时使用。 isSupportedmethod String 需要查询的方法value Boolean true 支持,false 不支持。由于历史原因,isSupported 方法目前返回value:"1/0"。selectContactselectedContacts String phone以逗号分隔。表示已经选择的联系人,用于显示联系人列表时自动选中。maxNum String 表示选择联系人的数量上限,默认为1(即单选),0 则表示不设上限。name String 联系人的名字phone Array 电话号码数组contacts Array 以数组形式返回联系人信息。原本的 name 和 phone 字段,则为数组中第一个联系人的信息(即兼容单选)。setOrientationorientation String 默认值为 portrait(强制竖屏),其他值包括 auto(跟随手机旋转),landscape(强制横屏)。setGestureBackpreventDefault Boolean false 表⽰执⾏默认⾏为(手势返回有效),true 表⽰阻⽌止默认⾏为(手势返回无效)。sendSMSphone Stringmessage Stringvalue Boolean true 成功,false 不成功。getCurrentPositionlatitude 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 responsescanBarcodevalue String 扫描得到的字符串sharePagetitle String 分享块标题desc String 分享块描述link String 分享块链接imgUrl String 分享块图片type String 分享类型;支持 weixin weibo pengyouquan sms email;如果是 all 表示想要分享到所有,此参数为空或不传此参数时,效果应等同于 all。value Boolean true 成功,false 不成功。trackEventevent String 记录事件名attrs String 统计字符串(JSON String)startPayquitpaymsg 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("支付异常");}});
loadingshow String 1 显示,0 隐藏text String 显示的文字SUCC 常量,正常显示FAIL 常量,显示失败CANCEL 常量,点了返回键value SUCC/FAIL/CANCELresumeprevUrl String 当前 webview 为基准,前一个 webview 的 URL(没有就为空)。nextUrl String 当前 webview 为基准,后一个 webview 的 URL。 backpreventDefault 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会覆盖前端定义的NativeAPIbuffer.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);