2023-09-06
未分类
00
请注意,本文编写于 335 天前,最后修改于 335 天前,其中某些信息可能已经过时。

目录

H5与原生通信的几种方式
1. userAgent
2. url
3. bridge通信
bridge原理
原生调用h5方法
h5调用原生代码
简易实现
观察者模式封装
HTML5+是什么?
原生可以对webview做些什么?

本文记录一些h5与原生通信的几种方法,bridge的原理,html5+的介绍。

相对原生开发,h5有一些不可替代的优势,如快速开发(跨平台),没有旧版本包袱(立即修复线上问题),会有很多场景通过app内嵌h5的方式进行开发。

然而相对于原生h5的能力自然弱一些,主要表现两个方面:

  1. 移动端的一些特性上,如推送、定位、打开设置、语音、视频、支付、拍照、打开三方app、新开webview等,虽然有些能力如 定位、拍照、支付等h5也可以实现,但体验上不及原生;
  2. 也是为了提升用户体验需要使用app的能力,如登录、退出登录、获取用户信息、加入购物车、跳转到原生页面等。

这时候就需要h5与原生结合混合开发, 不仅可以提高开发效率,也能增强h5能力,获得相对不错的用户体验。

H5与原生通信的几种方式

1. userAgent

  • android/ios 均可以修改 navigator.userAgent
  • 一般在原始值的基础上增加一下内容,如表示app版本,其他一些重要的标志id等也可放进去

2. url

  • h5 可以通过 location.href = 'appName://method?k=v'跳转到原生页面,或调用原生方法
  • 原生可以通过url query参数 把一些重要的信息如userIdtoken等传给h5,该方式安全上略差一些
  • android可以监听url变化(如更新原生导航头的需求),ios好像无法监控h5单页跳转

3. bridge通信

bridge原理

原生调用h5方法

app进行协商定义,H5暴露全局方法提供给app调用。

javascript
window.sdk = { double = value => value * 2, triple = value => value * 3, };

android 代码

java
webview.evaluateJavascript('window.sdk.double(10)', new ValueCallback <String>() { @Override public void onReceiveValue(String s) { // 20 } });

ios代码

objectivec
NSString *func = @"window.sdk.double(10)"; NSString *str = [webview stringByEvaluatingJavaScriptFromString:func]; // 20

h5调用原生代码

apph5 注入一个全局 js 对象,然后在 h5 直接访问这个对象

android

java
webview.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

javascript
window.appSdk.double(10);

有了h5原生相互通信的能力后,还有一个问题要考虑,就是何时触发,由谁开始呢?

简易实现

  1. 首先由 app 自定义sheme协议,比如 appName://action?params
  2. 然后打开了webview页面,h5js加载完成后并在合适的时间(比如用户点击了拍照按钮)由h5发起一个自定义协议请求,比如 location.href='appName://double?value=10'
  3. app 拦截这个请求后,进行相应的操作,获取返回值
  4. app 调用 h5 中的回调函数,比如 window.bridge.getDouble(20);

在第2步中,由于一些原因

  • 原生组件永远在h5组件上面,除非原生暴露方法,否则h5无法操作原生组件。如原生input输入框, 页面跳转输入框还在,且h5 遮罩无法覆盖原生input组件
  • 希望在h5展示原生组件而不是新开页面

这时候就需要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无法多次监听原声的同一事件,如推送通知、导航头操作等。
对于业务来说就的把与该事件有关的逻辑写在同一个回调方法里, 这种方式虽可能,但存在很强的耦合性。

这时候就需要使用观察着模式来接耦。
其实质维护一个数组,存储多个回调函数,原生通知的时候遍历数组依次调用每个回调函数。

先来看一个观察者模式

javascript
let 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 方法写死如下

javascript
window[callbackName] = function() { event.publish(callbackName, ...args); }

HTML5+是什么?

HTML5+官网

是对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

应用:

  1. 原生项目中引入5+APP的 SDK 替代bridge
    1. HTML5+ SDK功能概述(文章末尾有SDK接入的教程链接)
    2. 尽管 HTML5+规范定义了很多非常多的移动端特性的API,但业务总是有特殊性,可能需要写一些原生代码以方便JS调用,完成业务需求 5+ App开发Native.js入门指南
  2. 新项目使用5+APP开发 5+ Runtime

5+App是DCloud早期产品,新产品uni-app推出后,从功能、性能、生态全部超越5+App。
但5+App不要求使用vue,使用普通HTML即可开发。
一般商业级项目建议使用uni-app开发。 html5plus规范文档
对比5+App和uni-app的详细区别

总结:

  1. 它是一个跨iOSAndroid的混合开发App解决方案,与cordova类似。
  2. 它基于webview渲染,封装了大量原生能力给js API
  3. 加一条个人理解: 5+App是早期的技术,现在主流的混合开发技术有 Uni-app/React Native/Weex/Flutter体验上要优于 5+APPCordova但是早期的技术并不意味者过时或者被淘汰的技术,只是体验上相对新技术略差一些,相反往往越早诞生的技术,通常是最直接高效解决问题的方式(5+APP要比React Native开发成本低很多),很多公司都有业务采用APP内嵌h5的开发方式,5+APP提供的bridge要比很多公司自己封装bridge要优秀。技术变现应首要考虑投入产出比。

原生可以对webview做些什么?

本人最近在做一个IM项目,在两个APP中运行,用户和小哥可以相互聊天,有一个拍照的功能, 考虑成本问题,没有采用bridge方案(bridge要四个人开发呢),发现在android端存在问题,A app中可以正常拍照,在B app 中却没有反应
核心代码如下: <input type="file" capture accept="image/png,..." onchange="change">
经过查阅资料得知android原生可以控制webview的一些权限,于是补一补这块盲区

这里先记录一些学习查阅的链接,以后有做混合开发的项目再去研究

本文作者:郭敬文

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!