本文记录一些h5与原生通信的几种方法,bridge的原理,html5+的介绍。
相对原生开发,h5有一些不可替代的优势,如快速开发(跨平台),没有旧版本包袱(立即修复线上问题),会有很多场景通过app内嵌h5的方式进行开发。
然而相对于原生h5的能力自然弱一些,主要表现两个方面:
这时候就需要h5与原生结合混合开发, 不仅可以提高开发效率,也能增强h5能力,获得相对不错的用户体验。
android
/ios
均可以修改 navigator.userAgent
app
版本,其他一些重要的标志id
等也可放进去h5
可以通过 location.href = 'appName://method?k=v'
跳转到原生页面,或调用原生方法url query
参数 把一些重要的信息如userId
、token
等传给h5,该方式安全上略差一些android
可以监听url
变化(如更新原生导航头的需求),ios
好像无法监控h5
单页跳转与app
进行协商定义,H5
暴露全局方法提供给app
调用。
javascriptwindow.sdk = {
double = value => value * 2,
triple = value => value * 3,
};
android 代码
javawebview.evaluateJavascript('window.sdk.double(10)', new ValueCallback
<String>() { @Override public void onReceiveValue(String s) {
// 20
}
});
ios代码
objectivecNSString *func = @"window.sdk.double(10)";
NSString *str = [webview stringByEvaluatingJavaScriptFromString:func]; // 20
由 app
向 h5
注入一个全局 js
对象,然后在 h5
直接访问这个对象
android
javawebview.addJavascriptInterface( new Object () {
@JavascriptInterface
public int double (value) {
return value * 2;
}
@JavascriptInterface
public int triple(value) {
return value * 3 ;
}
}, "appSdk" );
ios
objectivec@interface AppSdk : NSObject {}
- ( int ) double :( int )value;
- ( int ) triple:( int )value;
@end
@implementation AppSdk
- ( int ) double :( int )value {
return value * 2
}
- (int) triple:(int)value {
return value * 3;
}
@end
JSContext *context = [webview valueForKeyPath:@ "documentView.webView.mainFrame.javaScriptContext" ];
AppSdk *appSdk = [AppSdk new];
context[@ "appSdk"] = appSdk;
javascript
javascriptwindow.appSdk.double(10);
有了h5原生相互通信的能力后,还有一个问题要考虑,就是何时触发,由谁开始呢?
app
自定义sheme
协议,比如 appName://action?params
webview
页面,h5
的js
加载完成后并在合适的时间(比如用户点击了拍照按钮)由h5
发起一个自定义协议请求,比如 location.href='appName://double?value=10'
app
拦截这个请求后,进行相应的操作,获取返回值app
调用 h5
中的回调函数,比如 window.bridge.getDouble(20);
在第2步中,由于一些原因
input
输入框, 页面跳转输入框还在,且h5
遮罩无法覆盖原生input
组件这时候就需要iframe
上场了, h5
可以通过移除iframe
进而移除原生组件。
下面是一个示例
html<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<title>Document</title>
</head>
<body>
<button id="btn1">扫一扫</button>
<button id="btn2">分享</button>
<script type="text/javascript">
// invoke.js
(function (window, undefined) {
// 调用 schema 的封装
function _invoke(action, data, callback) {
// 拼装 schema 协议
var schema = 'myapp://utils/' + action
// 拼接参数
schema += '?a=a'
var key
for (key in data) {
if (data.hasOwnProperty(key)) {
schema += '&' + key + data[key]
}
}
// 处理 callback
var callbackName = ''
if (typeof callback === 'string') {
callbackName = callback
} else {
callbackName = action + Date.now()
window[callbackName] = callback
}
schema += 'callback=callbackName'
// 触发
var iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = schema // 重要!
var body = document.body
body.appendChild(iframe)
setTimeout(function () {
body.removeChild(iframe)
iframe = null
})
}
// 暴露到全局变量
window.invoke = {
share: function (data, callback) {
_invoke('share', data, callback)
},
scan: function (data, callback) {
_invoke('scan', data, callback)
}
}
})(window)
</script>
<script type="text/javascript">
document.getElementById('btn1').addEventListener('click', function () {
window.invoke.scan({}, function () {})
})
document.getElementById('btn2').addEventListener('click', function () {
window.invoke.share({
title: 'xxx',
content: 'yyy'
}, function (result) {
if (result.errno === 0) {
alert('分享成功')
} else {
alert(result.message)
}
})
})
</script>
</body>
</html>
上述模式存在局限性,h5无法多次监听原声的同一事件,如推送通知、导航头操作等。
对于业务来说就的把与该事件有关的逻辑写在同一个回调方法里, 这种方式虽可能,但存在很强的耦合性。
这时候就需要使用观察着模式来接耦。
其实质维护一个数组,存储多个回调函数,原生通知的时候遍历数组依次调用每个回调函数。
先来看一个观察者模式
javascriptlet pool = [];
const subscribe = (type, callback, isCapture = false) => {
pool.push({
type, callback, isCapture
});
};
const unsubscribe = (type) => {
if (typeof type === 'function') {
pool = pool.filter((o) => o.callback !== type);
} else {
pool = pool.filter((o) => o.type !== type);
}
};
const publish = (type, args) => {
let isCapture = false;
pool.filter((o) => o.type === type).forEach((o) => {
isCapture = o.isCapture
result = o.callback && o.callback(args);
});
return result; // 需要返回给app结果的事件,如阻止webview关闭,阻止页面返回的
};
export const event = { subscribe, unsubscribe, publish };
// 监听 相当于element.addEventListener()
event.subscribe('click', console.log, false)
// 取消监听 相当于element.removeEventListener()
event.unsubscribe('click', console.log, false)
修改_invoke方法, 将函数体里面的 callback 方法写死如下
javascriptwindow[callbackName] = function() {
event.publish(callbackName, ...args);
}
是对HTML5
的一种扩展,提供一些API
以满足webApp
开发的需要。使得js
可以调用各种浏览器无法实现或实现不佳的系统能力,设备能力如摄像头、陀螺仪、文件系统等,业务能力如上传下载、二维码、地图、支付、语音输入、消息推送等。
HTML5 Plus移动App,简称
5+App
,是一种基于HTML、JS、CSS编写的运行于手机端的App,这种App可以通过扩展的JS API任意调用手机的原生能力,实现与原生App同样强大的功能和性能。HTML5 Plus规范
- 通过HTML5开发移动App时,会发现HTML5很多能力不具备。
- 为弥补HTML5能力的不足,在W3C中国的指导下成立了www.html5plus.org组织,推出HTML5+规范。
- HTML5+规范是一个开放规范,允许三方浏览器厂商或其他手机runtime制造商实现。
更多关于 5+App
应用:
5+App是DCloud早期产品,新产品uni-app推出后,从功能、性能、生态全部超越5+App。
但5+App不要求使用vue,使用普通HTML即可开发。
一般商业级项目建议使用uni-app开发。 html5plus规范文档
对比5+App和uni-app的详细区别
总结:
iOS
、Android
的混合开发App解决方案,与cordova
类似。webview
渲染,封装了大量原生能力给js API
。5+App
是早期的技术,现在主流的混合开发技术有 Uni-app
/React Native
/Weex
/Flutter
体验上要优于 5+APP
与Cordova
。 但是早期的技术并不意味者过时或者被淘汰的技术,只是体验上相对新技术略差一些,相反往往越早诞生的技术,通常是最直接高效解决问题的方式(5+APP
要比React Native
开发成本低很多),很多公司都有业务采用APP内嵌h5的开发方式,5+APP
提供的bridge
要比很多公司自己封装bridge
要优秀。技术变现应首要考虑投入产出比。本人最近在做一个IM项目,在两个APP中运行,用户和小哥可以相互聊天,有一个拍照的功能, 考虑成本问题,没有采用bridge方案(bridge要四个人开发呢),发现在android端存在问题,A app中可以正常拍照,在B app 中却没有反应
核心代码如下:<input type="file" capture accept="image/png,..." onchange="change">
经过查阅资料得知android原生可以控制webview的一些权限,于是补一补这块盲区
这里先记录一些学习查阅的链接,以后有做混合开发的项目再去研究
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!