作者:徐嘉伟(garyjwxu),2013年毕业后加入腾讯,曾先后主导负责财付通主站生活类板块、微信信用卡还款、H5支付中心、手Q AA收款、手Q红包、QQ钱包商户平台的前端开发工作,现为FIT金融市场部前端开发工程师。
本文选自《程序员》,更多精彩文章请订阅2016年《程序员》。

跨平台,是HTML5最重要的能力之一。而Hybrid H5因强依赖于具体App,往往不具有跨平台性。这时,将强依赖关系解耦,即可恢复HTML5的跨平台能力。近期我负责手Q红包打赏项目的前端开发,因项目涉及到多App跨平台兼容,对Hybrid H5的跨平台性有了一定的感悟和思考。在这里做下总结分享,希望能对大家有所收获。

Hybrid H5跨平台性

进入正题之前,先解释下本文主题的两个名词。

  1. Hybrid H5,即混合了原生能力的HTML5。区别于纯粹Web端的HTML5,它可调用原生的能力,强依赖于具体原生App,与原生共同构建整个App的UI层,是App UI层很好的灵活性补充。微信和手Q上的HTML5业务一般都属于Hybrid H5的范畴。
  2. 跨平台性,即一个HTML5页面可同时运行在多个平台上。可运行平台越多,跨平台性就越强。在如今移动互联网的发展大潮中,HTML5能与体验更优的原生终端齐步并进,其跨平台性可谓功不可没。

因强依赖于具体App,Hybrid H5往往不具有跨平台性。

本文将从Hybrid H5与原生的通讯原理出发,逐步探讨如何通过解耦来恢复Hybrid H5的跨平台性。

Hybrid H5与原生的通讯原理

Hybrid H5页面与原生应用的通讯原理图

从原理图中,有四个关键点:一个通讯媒介——原生自定义的通讯协议,以及围绕着通讯媒介执行的三个通讯行为——触发、调用、回调。

关键点详解

1.通讯媒介——原生通讯协议:原生自定义的伪协议,一般会定义成与HTTP协议类似的格式:

协议名://接口路径?参数1=XXX&参数2=XXX&参数3=XXX#callback

其中:

(1)协议名:APP自定义的协议名,用于HTML5触发行为的监控捕获,如手Q使用的 jsbridge://;

(2)接口路径:原生具体能力路径,不同原生能力路径不同;

(3)参数1=XXX&参数2=XXX&参数3=XXX#callback:HTML5传参与回调方法标识;

根据通讯协议规范,即可针对不同的原生能力给HTML5提供不同的调用地址,如:

jsbridge://method?a=2&b=3#h5MethodTag

2.通讯行为——触发:能被原生监听并捕获截拦的HTML5行为,都可以作为原生通讯协议的触发行为。

Hybrid H5的这类行为有console.log、alert、confirm、prompt、location.href等。将原生协议内容通过其中的某一行为触发,即可被原生正确捕获并解析。如:

location.href ='jsbridge://method?a=2&b=3#h5MethodTag'

HTML5调用后,原生终端会捕获到内容:jsbridge://method?a=2&b=3#h5MethodTag。

3.通讯行为——调用:原生终端根据HTML5传过来的内容,解析匹配后会路由到具体处理方法,执行原生能力逻辑。以iOS 为例(Swift语言),“调用”逻辑如下:

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) - Bool {let url = request.URL //urllet scheme = url ? .scheme //协议名let method = url ? .host //接口路径let query = url ? .query //参数if url != nil && scheme == "jsbridge" {/*根据method路由*/switch method!{case "method":self.method()case "openTenpayView":self.openTenpayView()...其他方法...default:}return false} else {return true}
}

原生终端根据捕获到的协议内容,进行解析获取,若伪协议为原生指定的伪协议(“jsbridge”),就会根据method内容和query参数进行路由操作,寻找具体的方法执行逻辑。否则,忽略处理,按照webview原有跳转逻辑处理。以第2步触发的伪协议内容为例,在本例“调用”代码中被原生捕获后,会路由执行逻辑:self.method();

4.通讯行为——回调:原生根据HTML5传过来的内容,捕获JavaScript回调函数方法名,在原生逻辑执行结束后,将执行结果带到回调函数中并执行JavaScript回调函数。通过在第3步“调用”执行完后,iOS会调用JavaScript回调函数H5MethodTag:

/*解析到H5的回调函数名为H5MethodTag(#号后内容),回调执行js的方法*/
webview.stringByEvaluatingJavaScriptFromString("H5MethodTag(data)") 

通过以上4个关键点,即可做到HTML5与原生终端的相互通讯,完成HTML5对原生能力的调用。

初次解耦:App内跨平台——创建JS API解耦通讯逻辑、封装平台差异

由上述通讯原理了解到,Hybrid H5直接调用定义好的原生通讯协议,即可完成通讯全过程。但这里有一个明显的问题,即Hybrid H5会强耦合于当前平台。不说跨App了,App内跨平台(Android、iOS、WP)都会显示吃力。这里面有很多原因,其中一个较明显的原因在于,不同平台App开发团队通讯协议规范定义存在不一致。再者,HTML5业务代码上满满的类似 JSONP的协议调用,也并不好维护。

要达到Hybrid H5在App内跨平台,业界常见做法是App对外提供JS API。通过JS API将各平台协议规范差异进行封装,解耦通讯逻辑,并以函数接口的方式提供给Hybrid H5调用。JS API接口一般会定义成如下格式:

ns.method({/*cfg参数对象*/
}, function(data) {/*回调*/
})

通过JS API实现Hybrid H5在App内跨平台的原理图

原理核心:HTML5与原生通讯之间增加一层JS API,JS API完成三大行为:API接口创建、协议URL组装、创建iframe发起伪协议请求。

因手Q的JS API相对比较成熟,下面会以手Q JS API中的核心源码进行分析。

1.API接口创建:JavaScript函数接口封装、平台差异处理,方便HTML5函数调用。

mqq.build('mqq.tenpay.openTenpayView', {iOS: function(options, callback) {var callbackName = callback ? mqq.callback(callback) : null;mqq.invokeClient('pay', 'openTenpayView', {'params': options,'callback': callbackName});},android: function(params, callback) {mqq.invokeClient('pay', 'openTenpayView', JSON.stringify(params), callback);},supportInvoke: true,support: {iOS: '4.6.1',android: '4.6.1'}
});

mqq.build方法为API接口创建方法。通过传入待创建的JS API方法名(mqq.tenpay.openTenpayView)和不同平台(Android、iOS)的差异处理配置。最终会生成HTML5可调用方法:

mqq.tenpay.openTenpayView({/*data*/
},function(ret){/*callback*/
})

2.协议URL组装:从接口到URL协议的转换、回调处理,完成协议URL创建。

第1步中,不同平台差异处理都会调用mqq.invokeClient方法,该方法实际处理的就是原理图中与原生通讯的过程。我们先来看协议URL组装的过程。

/*生成回调索引*/
sn = storeCallback(callback);
/*协议路径组装*/
url = 'jsbridge://' + encodeURIComponent(ns) + '/' + encodeURIComponent(method);
/*参数组装*/
argus.forEach(function(a, i) {if (exports.isObject(a)) {a = JSON.stringify(a);}if (i === 0) {url += '?p=';} else {url += '&p' + i + '=';}url += encodeURIComponent(String(a));
});
/*回调函数索引组装*/
url += '#' + sn;
/*链接调用*/
result = openURL(url, ns, method);

协议URL组装的过程实际上是对传入参数按协议规范进行拼串的过程,其中包括匿名回调函数的回调索引创建、协议名&协议路径拼串、传参循环遍历拼串。

3.创建iframe发起伪协议请求:请求触发。

/*创建隐藏iframe*/
var iframe = document.createElement('iframe');
iframe.style.cssText = 'display:none;width:0px;height:0px;';function failCallback() {/*错误处理*/
}
/*iframe协议调用*/
iframe.onload = failCallback;
iframe.src = url;
(document.body || document.documentElement).appendChild(iframe);/*删除iframe*/
setTimeout(function() {iframe && iframe.parentNode && iframe.parentNode.removeChild(iframe);
}, 0);

通过创建iframe来完成协议调用,并在调用结束后将iframe删除,即可在不影响原HTML5流程的情况下完成调用全过程。

再次解耦:App间跨平台——JS API细化,封装App差异

通过上述的解耦处理,Hybrid H5已经可以在App内各平台运行了。但往往这种JS API是App级提供的JS API(下面简称App JS API),App JS API并不会去兼容别的App的差异。而实际情况具体到某一Hybrid H5,尤其是与App外部合作的Hybrid H5,则并不仅仅只运行在一个App上。比如信用卡还款业务,微信有,手Q 也有,功能都一样。这种情况就需要进一步的解耦,从业务侧再抽离一层JS API(下面简称 H5 JS API)来处理App间的差异,而非每个App各自一套HTML5。

利用从业务侧抽离出的H5 JS API来处理App间的差异,原理示意图

原理核心:Hybrid H5业务上增加多一层自维护的 H5 JS API,H5 JS API完成两大行为:App JS API差异请求、App JS API差异封装。

1.App JS API差异请求:根据当前运行环境App请求具体的App JS API。

下面以Hybrid H5需同时运行在手Q和空间独立版的App JS API差异请求处理逻辑。

<script type="text/javascript" >(function() {var ua = navigator.userAgent || "",isQQ = ua.match(/QQ\/([\d\.]+)/),isQzone = ua.match("Qzone");if (isQQ) {document.write("<script src='https://open.mobile.qq.com/sdk/qqapi.js?_bid=152'><\x2Fscript>");} else if (isQzone) {document.write("<script src='https://qzonestyle.gtimg.cn/qzone/phone/m/v4/widget/mobile/jsbridge.js'><\x2Fscript>");} else {// 不是已兼容app,跳转到兼容app上运行var currentHref = window.location.href;/*跳转到手Q打开本页面*/window.location.href = 'mqqapi://forward/url?url_prefix=' + btoa(currentHref) + '&version=1&src_type=web';/*该页面支持自定义弹层*/setTimeout(function() {var _tempBox = confirm('请在手机QQ中使用~');if (_tempBox == true) {/*跳至手Q打开*/window.location.href = 'mqqapi://forward/url?url_prefix=' + btoa(currentHref) + '&version=1&src_type=web';}}, 0)}})()</script>

除了对需兼容的App进行差异请求外,还应对在不兼容的App运行时做跳转到主兼容App打开当前页面的逻辑处理,并做引导性提示,保障页面的完整可用性。

2.App JS API差异封装:根据当前具体运行的平台调用相应的App JS API 接口。H5 JS API的接口形式尽量与主运行App的JS API保持一致

下面以打开QQ钱包原生页和原生页面跳转能力为例,做App JS API的差异封装。

var mod = {...openTenpayView: function(param, callback) {if (isQQ) {var param = $.extend({userId: $.getCookie('uin').replace(/^o0*/, '')}, param);mqq.tenpay.openTenpayView(param, callback);} else {/*调起手Q打开中转页jump.html,由中转页打开原生功能页*/var targetHref = 'http://testhost.com/jump.html?go=' + param.viewTag + '&_wv=' + (1 + 2 + 1024 + 2097152 + 33554432); //跳转url/*跳到手Q*/window.location.href = 'mqqapi://forward/url?url_prefix=' + btoa(targetHref) + '&version=1&src_type=web';}},openUrl: function(paramObj) {if (isQQ) {mqq.ui.openUrl({url: paramObj.url,target: 1});} else if (isQzone) {mqq.invoke("ui", "openUrl", {url: paramObj.url,target: 1,style: 1});} else {/*兼容处理*/location.href = paramObj.url}},...其他接口...
};
return mod;

调用openTenpayView,页面能在手Q中正常调用,而在非手Q时则跳转回手Q打开处理;

调用openUrl,对于手Q和空间独立版做相应的接口调用,而其他平台则直接使用HTML5的 location 跳转。

总结

HTML5本质是具有跨平台性的。Hybrid H5因混合了原生能力,强耦合于原生,不再具有跨平台性。要恢复其跨平台能力,关键在解耦,将其耦合于原生的部分解耦封装起来。

解耦是开发很重要的一项能力,Hybrid H5跨平台性的回归正得益于解耦的处理。

因耦合而导致某项能力减弱的情况还有很多,比如HTML5的灵活性等等。遇到这种情况大家不妨也试试解耦,或许会收到意想不到的惊喜。


订阅2016年程序员(含iOS、Android及印刷版)请访问 http://dingyue.programmer.com.cn

订阅咨询:

• 在线咨询(QQ):2251809102
• 电话咨询:010-64351436
• 更多消息,欢迎关注“程序员编辑部”

解耦——Hybrid H5跨平台性思考相关推荐

  1. 江哥html阶段测试,来自江哥的H5+跨平台开发实战课程 跟着江哥从零狂虐H5+跨平台开发视频教程...

    课程内容是完整的,只是目录收集和编号的有点乱,大家下载后的都是正常的,不用担心 (28)\\02-跟着江哥狂虐H5+跨平台开发系列-认识HTML-第一次更新\\视频:目录中文件数:9个 ├─(28) ...

  2. 第 1 章 一 JVM 和 Java 体系架构 (虚拟机介绍、字节码、Java跨平台性)

    第 1 章 JVM 和 Java 体系架构 1.JVM 前言 作为Java工程师, 你是否也遇到过这些问题? 运行着的线上系统突然卡死,系统无法访问,甚至直接OOM! 想解决线上JVM GC问题,但却 ...

  3. Java 版本、语言规范、API、JDK、IDE、Java 源程序编译、执行原理(跨平台性根本原因)、特殊字符用法、8 大数据类型小结

    文章目录 前言 一.三大版本类型 二.Java 语言规范 三.应用程序接口(API) 四.Java 开发工具包(JDK) 五.集成开发环境(IDE) 六.Java 运行环境(JRE) 七.Java 源 ...

  4. Java语言的跨平台性或Java语言为什么说可以跨平台运行

    什么叫跨平台性? 一个编译好的.class文件可以在多个系统下运行,这种特性称为跨平台.  Java的运行机制 实现Java程序跨平台运行的核心是JVM虚拟机,不同的环境下安装不同版本的JVM,那么同 ...

  5. (1) Dot net“很难”实现像Java一样的跨平台性

    大犬 .net"很难"实现像Java一样的跨平台性 已发布: 2004-12-11 下午8:05   回复 前两天在网上看了一个帖子,大概意思是: Java在今后很长的一段时间将是 ...

  6. java的跨平台性是什么?原理是什么?

    跨平台性 Java 的跨平台性是指一个 Java 程序可以在不同的操作系统和硬件平台上进行部署和运行,而无需对源代码进行任何修改或重新编译.这有赖于 Java 虚拟机(JVM)的存在. 原理 JVM ...

  7. 简述java的跨平台性是什么,为什么具有跨平台性

    1.简述java的跨平台性是什么,为什么具有跨平台性 java源代码可以通过虚拟机编译运行在任何的系统上,也不是任何系统,只要该系统能够安装java虚拟机就能运行java程序.

  8. java的跨平台性与c语言,Java为什么具有跨平台性?

    1.先回顾一下初出茅庐的时候 在我刚刚开始接触到Java的时候,印象比较深刻的就是面向对象,跨平台性,至于跨平台性,当然,也稍微了解到了Java背后那个大神! JVM!Java虚拟机 但是连基本语法都 ...

  9. Hybrid App 跨平台热更新方案实践 附带源码

    前言 移动开发的跨平台与快速发布一直是开发者的追求,也是技术的一个发展趋势,现在各大厂开始有了自己的大前端团队,所以我们也开始了自己的探索,目前来说主要有两种思路: Hybrid App 代表:Cor ...

  10. 手机 html5 语音输入,整合vue开发H5+跨平台app (以开发语音识别为例)

    HBuilder快速搭建H5+应用 上一次尝试了下H5+开发了跨平台app,能够发现的事利用H5+开发app的话,其实就是利用h5将页面给作出来,而后借助H5+的sdk和原生系统作交互,而咱们开发人员 ...

最新文章

  1. zTree实现节点修改的实时刷新
  2. 干货 | 谷歌BERT模型fine-tune终极实践教程
  3. python04-列表与元祖
  4. XML——XML Schema
  5. jsp mysql 插入数据_jsp连接MySQL实现插入insert操作功能示例
  6. 连续3年入围 Gartner 容器竞争格局,阿里云容器服务新布局首次公开
  7. tsv文件导oracle窜列,TSV(tsv文档怎么转换excel)
  8. Java编程:排序算法
  9. 路灯干扰者视频2021-08-10
  10. laydate 在vue中使用_如何在vue-cli项目里用layer的layData日期组件(代码)
  11. 经纬财富:沧州散户炒白银必读
  12. Wannacry“永恒之蓝”勒索病毒最全防范措施
  13. 计算机在it行业的应用,IT行业的前景怎样啊,我是自考本科毕业,计算机应用...
  14. [导入]《玫瑰人生》【第80届奥斯卡获奖大片】【DVD中英字幕】【14:50发布】
  15. 深入浅出,一篇超棒的机器学习入门文章
  16. 服务器system文件缺失,incaseformat蠕虫病毒爆发,警惕文件丢失!
  17. 干货 | 日访问过亿,办公IM及开放式平台在携程的实践
  18. XCode所有版本下载方法
  19. C语言中void fun() 与 int fun()的区别
  20. 力控批量添加变量_力控常见问题大全

热门文章

  1. windows家庭版关闭windows defender
  2. 斯蒂文斯理工学院计算机专业,斯蒂文斯理工学院的计算机专业如何?
  3. 王者战力接口(免费),附接口文档+小程序源码
  4. 云+智能的羊群效应显现,百度用“云智一体”坐定头羊位置
  5. 笔耕不辍,无问西东——2018年终小结
  6. 「笔耕不辍」常见远程调用协议
  7. 挑战年薪50万不是梦,你要的高级架构师课程来袭!
  8. 洛谷3966 BZOJ3172 单词 AC自动机
  9. 米联ZYNQ7Z020系列的以太网网口芯片RTL8211FD自适应速度的问题
  10. 织梦建站教程dedecms标签调用大全【经典教程】