背景

来自我司业务方要求,需开发一款APP。但由于时间限制,只能采取套壳app方式,即原生app内嵌webview展示前端页面。本文主要记述JavaScript与原生app间通信,以及内嵌webview开发时,前端方面可能踩的一些坑。

技术架构

前端:vue+vuex+vue-router+webpack全家桶开发
后端:Node(express框架)简单转发接口至java-真后端接口。

js与原生通信

采用jsBridge技术和原生APP通信
android 传送门 和ios 传送门,因为两个平台初始化方式不同,因此在开发过程中,需针对每个平台做对应操作。 具体做法

  1. 按照库要求,声明好初始化函数
//android
function connectWebViewJavascriptBridge{if (window.WebViewJavascriptBridge) {//do your work here} else {document.addEventListener('WebViewJavascriptBridgeReady', function(){//do your work here},false);}
}
//ios
setupWebViewJavascriptBridge(function(bridge){/* Initialize your app here */bridge.registerHandler('JS Echo', function(data, responseCallback){console.log("JS Echo called with:", data)responseCallback(data)})bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData){console.log("JS received response:", responseData)})
})
  1. 初始化,得到bride对象。则可调用原生app已定义方法或注册js方法供原生调用
setupWebViewJavascriptBridge(function(bridge){/* Initialize your app here */bridge.registerHandler('JS Echo', function(data, responseCallback){console.log("JS Echo called with:", data)responseCallback(data) //})bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData){console.log("JS received response:", responseData)})
})

Tips:

  • Android 与 IOS初化方式不同,需要判断平台后再进行调用。另外Android初始化时,需额外引入一些方法。
  • 调用Android定义方法时,返回值只能为字符串。而IOS可为JSON对象。需要在callHandler时,对返回值进行封装处理或统一规定好数据格式。
  • 完整业务代码文末给出

踩坑

  1. 调用bridge属性方法registerHandler,callHandler,在回调函数内处理页面逻辑时,最好避免使用this
  2. vue组件下,在registerHandler,callHandler回调函数内使用vue实例时,无法获取实例对象。正确做法是在回调函数内调用window对象下方法,再通过该方法去使用vue实例对象。
//vue 组件
mounted(){window['handleServicePushMessage'] = (res) => {vm.handleServicePushMessage(res)};bridge.registerHandler("servicePushMessage", function (data, responseCallback){handleServicePushMessage(data)responseCallback(data) //可传值到App})
}
  1. 桌面推送消息点击跳转至App内详情情况下,js注册方法供调用时,可能会引起重复调用的问题。故在方法内需做好重复调用判断
  2. IOS-12.0版本下,在有输入框的页面,输入时软键盘会顶起webview,当失去焦点时,webview不会自动回弹。需调用APP做处理拉回界面。
//解决ios 12版本 ui不自动回拉问题
document.addEventListener('focusout', function (event){let curTarget = event.target || event.srcElement;let isInput= ['input', 'textarea'];//处理页面连续点击都为输入框的情况let curTargetTagName= curTarget.tagName.toLowerCase();if (isInput.includes(curTargetTagName)) {//事件处理//延迟获取activeElement再进行判断setTimeout(function (){let activeEle = document.activeElement;let activeEleTagName= activeEle.tagName.toLowerCase();if (!isInput.includes(activeEleTagName)) {// console.log(document.activeElement.tagName);//调用app桥拉回webviewperformMethod('scrollTotop', null);}}, 200);}
}, true);

5.当js调用app不存在的桥时,无法捕获异常,页面不会报错
6.导航栏显示问题,由于项目时间紧迫,并且app开发人员不承载太多开发任务,所以路由控制放在前端处理。此时就有导航栏电池时间栏的适配问题。本项目采用顶部下调20PX处理,电池时间栏字体颜色的控制也是通过桥调用来设置;另外iPhone X适配另外处理。
7.当app加载完网页时,js立即调用原生方法桥时,可能出现原生方法桥未注册完情况。故特殊情况需延迟调用桥操作。

完整代码

/*判断平台*/
function (window){window.device = {};var ua = navigator.userAgent;var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;if (android) {device.os = 'android';device.osVersion = android[2];device.android = true;device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0}if (ipad || iphone || ipod) {device.os = 'ios';device.ios = true}
}(window)
/*引入Android需要的初始化,IOS不执行,如执行IOS端桥调用会受影响*/
(function (){if (window.WebViewJavascriptBridge || device.ios) {return false;}var messagingIframe;var sendMessageQueue = [];var receiveMessageQueue = [];var messageHandlers = {};var CUSTOM_PROTOCOL_SCHEME = 'yy';var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';var responseCallbacks = {};var uniqueId = 1;function _createQueueReadyIframe(doc){messagingIframe = doc.createElement('iframe');messagingIframe.style.display = 'none';doc.documentElement.appendChild(messagingIframe);}/*set default messageHandler*/function init(messageHandler){if (WebViewJavascriptBridge._messageHandler) {throw new Error('WebViewJavascriptBridge.init called twice');}WebViewJavascriptBridge._messageHandler = messageHandler;var receivedMessages = receiveMessageQueue;receiveMessageQueue = null;for (var i = 0; i < receivedMessages.length; i++) {_dispatchMessageFromNative(receivedMessages[i]);}}function send(data, responseCallback){_doSend({data: data}, responseCallback);}function registerHandler(handlerName, handler){messageHandlers[handlerName] = handler;}function callHandler(handlerName, data, responseCallback){_doSend({handlerName: handlerName, data: data}, responseCallback);}/*sendMessage add message, 触发native处理 sendMessage*/function _doSend(message, responseCallback){if (responseCallback) {var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();responseCallbacks[callbackId] = responseCallback;message.callbackId = callbackId;}sendMessageQueue.push(message);messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;}/* 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容*/function _fetchQueue(){var messageQueueString = JSON.stringify(sendMessageQueue);sendMessageQueue = [];/*android can't read directly the return data, so we can reload iframe src to communicate with java*/messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);}/*提供给native使用,*/function _dispatchMessageFromNative(messageJSON){setTimeout(function (){var message = JSON.parse(messageJSON);var responseCallback;/*java call finished, now need to call js callback function*/if (message.responseId) {responseCallback = responseCallbacks[message.responseId];if (!responseCallback) {return;}responseCallback(message.responseData);delete responseCallbacks[message.responseId];} else {/*直接发送*/if (message.callbackId) {var callbackResponseId = message.callbackId;responseCallback = function (responseData){_doSend({responseId: callbackResponseId, responseData: responseData});};}var handler = WebViewJavascriptBridge._messageHandler;if (message.handlerName) {handler = messageHandlers[message.handlerName];}/*查找指定handler*/try {handler(message.data, responseCallback);} catch (exception) {if (typeof console != 'undefined') {console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);}}}});}/*提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以*/function _handleMessageFromNative(messageJSON){if (receiveMessageQueue && receiveMessageQueue.length > 0) {receiveMessageQueue.push(messageJSON);} else {_dispatchMessageFromNative(messageJSON);}}var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {init: init,send: send,registerHandler: registerHandler,callHandler: callHandler,_fetchQueue: _fetchQueue,_handleMessageFromNative: _handleMessageFromNative};var doc = document;_createQueueReadyIframe(doc);var readyEvent = doc.createEvent('Events');readyEvent.initEvent('WebViewJavascriptBridgeReady');readyEvent.bridge = WebViewJavascriptBridge;doc.dispatchEvent(readyEvent);
})();
/*Android端初始化函数*/
function connectWebViewJavascriptBridge(callback){if (window.WebViewJavascriptBridge) {callback(WebViewJavascriptBridge)} else {document.addEventListener('WebViewJavascriptBridgeReady', function (){callback(WebViewJavascriptBridge)}, false);}
}
/*IOS端初始化函数*/
function setupWebViewJavascriptBridge(callback){if (window.WebViewJavascriptBridge) {return callback(WebViewJavascriptBridge)} else {}if (window.WVJBCallbacks) {return window.WVJBCallbacks.push(callback)}window.WVJBCallbacks = [callback];var WVJBIframe = document.createElement('iframe');WVJBIframe.style.display = 'none';WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';document.documentElement.appendChild(WVJBIframe);setTimeout(function (){document.documentElement.removeChild(WVJBIframe)}, 0)
}
if(device.ios){setupWebViewJavascriptBridge(function(bridge){/*挂载上全局对象*/window.BRIDGE= brige;})
}
if(device.android){connectWebViewJavascriptBridge(function(bridge){/*挂载上全局对象*/window.BRIDGE= brige;})
}

H5在WebView上开发小结相关推荐

  1. Android开发笔记(一百六十六)H5通过WebView录像上传

    前面的博文< Android开发笔记(一百五十二)H5通过WebView上传图片>介绍了如何拍照上传给网页,不料客户又要求再加个摄像上传给网页.既然如此,那么再探讨一下如何实现这个摄像上传 ...

  2. uni-app.04.发布成H5后,uni.chooseImage方法在android WebView上无法使用

    发布成H5后,uni.chooseImage方法在android WebView上无法使用 引言 解决方案 特别注意 引言 经过三个星期的折腾,uni-app的编码阶段宣告结束,正式进入到测试阶段.由 ...

  3. android 上传html文件大小,浅谈关于Android WebView上传文件的解决方案

    我们在开发需求的时候,难免会接入一下第三方的H5页面,有些H5页面是具有上传照片的功能,Android 中的 WebView是不能直接打开文件选择弹框的 接下来我讲简单提供一下解决方案,先说一下思路 ...

  4. pdfjs viewer 开发小结

    此文已由作者吴家联授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1. pdfjs库简介 PDF.js 是由Mozilla 主导推出的可以将PDF文件转换为H5页面进行展示的 ...

  5. react-native 开发小结(Android)

    在windows上开发react-native已经有一些时候了.作为一个Android原生开发者,在开发的过程中,虽然有点蛋疼,但毕竟积累了一点点经验,再不说出来,我就要侧漏了...... 1,前言 ...

  6. Android 即时通讯开发小结(二)

    <Android 即时通讯开发小结>基于IM Andriod 开发的各种常见问题,结合网易云信即时通讯技术的实践,对 IM 开发做一个全面的总结. 相关推荐阅读:. Android即时通讯 ...

  7. Android 即时通讯开发小结(一)

    本文将基于 IM Andriod 开发的各种常见问题,结合网易云信即时通讯技术的实践,对 IM 开发做一个全面的总结. 客户端架构 作为一个 IM 软件,最重要的一个特性就是保证消息的达到率和实时性. ...

  8. 第一个 iOS 项目开发小结 - SwiftUI 学习资料、开源项目

    第一个 iOS 项目开发小结 SwiftUI 知识点小记 学习资料 开源项目 一些问题记录 项目预览 这段时间由于项目需要,我学习了一段时间 SwiftUI 并单独完成了一个系统的开发,耗时两个星期, ...

  9. .Net Web微信H5鱼虾蟹网站开发搭建技术栈

    本文整理了当前企业web微信H5鱼虾蟹网站开发搭建(h5.fanshubbs.com)的管理系统架设Q1687054422,商城等系统的常用开发技术栈. C#常见运算符 一元运算符(+.-.!.~.+ ...

最新文章

  1. 《强化学习周刊》第25期:DeepMind提出无模型风险敏感强化学习、谷歌发布 RLDS数据集生态系统...
  2. 网络流三·二分图多重匹配 HihoCoder - 1393
  3. CentOS6.5安装Subversion
  4. UDP socket 设置为的非阻塞模式
  5. 通过rpm包安装、配置及卸载mysql的详细过程.
  6. 前端学习(3102):vue+element今日头条管理-hello-react案例
  7. nodejs在cmd提示不是内部或外部命令解决方法
  8. typora用Pandoc导出html,Typora安装 Pandoc实现导出功能
  9. Redis流水线性能提高
  10. ios退款 怎么定位到是哪个用户_哪个浏览器兼容性最好用?看看用户都是怎么评价的吧...
  11. python 中的metaclass和baseclasses
  12. 5-1 可维护性的度量和构造原则
  13. Hadoop1.1.2开发笔记(一)
  14. win10删除软件注册表
  15. Arduino 用4位共阴数码管造一个计数器
  16. 2018年11月11日学习日志
  17. D2. Two Hundred Twenty One (hard version)
  18. java加法的底层_常见开发语言加减乘除底层是如何做到的?
  19. android开发论坛!原生Android开发的路该怎么走?系列篇
  20. 皮影机器人ppt_第27届计算机表演赛—智能皮影机器人

热门文章

  1. c# 蓝牙虚拟串口_蓝牙模块——基础知识介绍
  2. 爬虫的增量式抓取和数据更新
  3. b端 ux 设计思维_借助系统思维从视觉设计过渡到UX
  4. IE开发人员工具无法使用
  5. 根据当月数据库自动生成下个月数据库--3
  6. linux启动时挂载rootfs的几种方式 .
  7. PHP实现微信随机红包算法和微信红包的架构设计简介
  8. Siamese Network理解
  9. [NOIP2015提高组]运输计划
  10. c#中的奇异递归模式