本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1275&extra=page%3D1
跨平台,是H5最重要的能力之一。而 HybrID H5 因强依赖于具体 app,往往不具有跨平台性。这时,将强依赖关系解耦,即可恢复 H5 的跨平台能力。近期本人负责 手Q 红包打赏项目的前端开发,因项目涉及到多 app 跨平台兼容,对 hybrID H5 的跨平台性有了一定的感悟和思考。在这里做下总结分享,希望能对大家有所收获。
HybrID H5 跨平台性进入正题之前,先解释下本文主题的两个名词:
①HybrID H5,即混合了原生能力的 H5。区别于纯粹 web 端的 H5,它可调用原生的能力,强依赖于具体原生 app,与原生共同构建整个 app 的 UI 层,是 app UI 层很好的灵活性补充。微信和 手Q 上的 H5 业务一般都属于 HybrID H5 的范畴。
② 跨平台性,即一个 H5 页面可同时运行在多个平台上。可运行平台越多,跨平台性就越强。在如今移动互联网的发展大潮中,H5 能与体验更优的原生终端齐步并进,其跨平台性可谓功不可没。
因强依赖于具体 app,HybrID H5 往往不具有跨平台性。
本文将从 HybrID H5 与原生的通讯原理出发,逐步探讨如何通过解耦来恢复 HybrID H5 的跨平台性。
HybrID H5 与原生的通讯原理原理图
从原理图中,有4个关键点:
1个通讯媒介——原生自定义的通讯协议;
以及围绕着通讯媒介执行的3个通讯行为——触发、调用、回调。
关键点详解
①通讯媒介——原生通讯协议:原生自定义的伪协议,一般会定义成与 http 协议类似的格式:
协议名://接口路径?参数1=XXX&参数2=XXX&参数3=XXX#callback
其中:
a、协议名:app 自定义的协议名,用于H5触发行为的监控捕获,如 手Q 使用的 JsbrIDge://;
b、接口路径:原生具体能力路径,不同原生能力路径不同;
c、参数1=XXX&参数2=XXX&参数3=XXX#callback:H5传参与回调方法标识;
根据通讯协议规范,即可针对不同的原生能力给H5提供不同的调用地址,如:
JsbrIDge://method?a=2&b=3#h5MethodTag
②通讯行为——触发:能被原生监听并捕获截拦的H5行为,都可以作为原生通讯协议的触发行为。
HybrID H5 的这类行为有 console.log、alert、confirm、prompt、location.href 等。将原生协议内容通过其中的某一行为触发,即可被原生正确捕获并解析。如:
location.href ='JsbrIDge://method?a=2&b=3#h5MethodTag'
H5调用后,原生终端会捕获到内容:JsbrIDge://method?a=2&b=3#h5MethodTag
③通讯行为——调用:原生终端根据 H5 传过来的内容,解析匹配后会路由到具体处理方法,执行原生能力逻辑。以 ios 为例(swift 语言),“调用”逻辑如下:
func webVIEw(webVIEw: UIWebVIEw,shouldStartLoaDWithRequest request: NSURLRequest,navigationType: UIWebVIEwNavigationType) - Bool { let url = request.URL //url let 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 原有跳转逻辑处理。以第②步触发的伪协议内容为例,在本例“调用”代码中被原生捕获后,会路由执行逻辑:self.method();
④通讯行为——回调:原生根据 H5 传过来的内容,捕获 Js 回调函数方法名,在原生逻辑执行结束后,将执行结果带到回调函数中并执行 Js 回调函数。通过在第③步“调用”执行完后,ios 会调用 Js 回调函数 H5MethodTag:
/*解析到H5的回调函数名为H5MethodTag(#号后内容),回调执行Js的方法*/webvIEw.stringByEvaluatingJavaScriptFromString("H5MethodTag(data)")
通过以上4个关键点,即可做到 H5 与原生终端的相互通讯,完成H5对原生能力的调用。
初次解耦:app 内跨平台——创建JsAPI解耦通讯逻辑、封装平台差异由上述通讯原理了解到,HybrID H5 直接调用定义好的原生通讯协议,即可完成通讯全过程。但这里有一个明显的问题,即 HybrID H5 会强耦合于当前平台。不说跨 app 了,app 内跨平台(androID/ios/wp)都会显示吃力。这里面有很多原因,其中一个较明显的原因在于,不同平台 app 开发团队通讯协议规范定义存在不一致。再者,H5 业务代码上满满的类似 Jsonp 的协议调用,也并不好维护。
要达到 HybrID H5 在 app 内跨平台,业界常见做法是 app 对外提供 JsAPI。通过 JsAPI 将各平台协议规范差异进行封装,解耦通讯逻辑,并以函数接口的方式提供给 HybrID H5 调用。JsAPI 接口一般会定义成如下格式:
ns.method({ /*cfg参数对象*/},function(data) { /*回调*/})
原理图
原理核心:H5 与原生通讯之间增加一层 JsAPI,JsAPI 完成三大行为:API 接口创建、协议 url 组装、创建 iframe 发起伪协议请求;
因 手Q 的 JsAPI 相对比较成熟,下面会以 手Q JsAPI 中的核心源码进行分析。
①API接口创建:Js 函数接口封装、平台差异处理,方便H5函数调用
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',JsON.stringify(params),callback); },supportInvoke: true,support: { iOS: '4.6.1',androID: '4.6.1' }});
mqq.build 方法为 API 接口创建方法。通过传入待创建的 JsAPI 方法名(mqq.tenpay.openTenpayVIEw)和不同平台(androID/ios)的差异处理配置。最终会生成H5可调用方法:
mqq.tenpay.openTenpayVIEw({ /*data*/},function(ret){ /*callback*/})
②协议url组装:从接口到 url 协议的转换、回调处理,完成协议 url 创建
第①步中,不同平台差异处理都会调用 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 组装的过程实际上是对传入参数按协议规范进行拼串的过程,其中包括匿名回调函数的回调索引创建、协议名&协议路径拼串、传参循环遍历拼串。
③创建 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 删除,即可在不影响原 H5 流程的情况下完成调用全过程。
再次解耦:app 间跨平台—— JsAPI 细化,封装 app 差异通过上述的解耦处理,HybrID H5 已经可以在 app 内各平台运行了。但往往这种 JsAPI 是 app 级提供的 JsAPI(下面简称 app JsAPI),app JsAPI 并不会去兼容别的 app 的差异。而实际情况具体到某一 HybrID H5,尤其是与 app 外部合作的 HybrID H5,则并不仅仅只运行在一个 app上。比如xyk还款业务,微信有,手Q 也有,功能都一样。这种情况就需要进一步的解耦,从业务侧再抽离一层 JsAPI(下面简称 H5 JsAPI)来处理 app 间的差异,而非每个 app 各自一套 H5。
原理图
原理核心:HybrID H5 业务上增加多一层自维护的 H5 JsAPI,H5 JsAPI 完成两大行为:app JsAPI 差异请求、app JsAPI 差异封装。
①app JsAPI 差异请求:根据当前运行环境 app 请求具体的 app JsAPI
下面以 HybrID H5 需同时运行在手Q和空间独立版的 app JsAPI 差异请求处理逻辑。
<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'; /*该页面支持自定义d层*/ 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 打开当前页面的逻辑处理,并做引导性提示,保障页面的完整可用性。
②app JsAPI 差异封装:根据当前具体运行的平台调用相应的 app JsAPI 接口。H5 JsAPI 的接口形式尽量与主运行 app 的 JsAPI 保持一致
下面以打开 QQ 钱包原生页和原生页面跳转能力为例,做 app JsAPI 的差异封装。
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 和空间独立版做相应的接口调用,而其他平台则直接使用 H5 的 location 跳转。
总结H5 本质是具有跨平台性的。HybrID H5 因混合了原生能力,强耦合于原生,不再具有跨平台性。要恢复其跨平台能力,关键在解耦,将其耦合于原生的部分解耦封装起来。
解耦是开发很重要的一项能力,HybrID H5 跨平台性的回归正得益于解耦的处理。
因耦合而导致某项能力减弱的情况还有很多,比如 H5 的灵活性等等。遇到这种情况大家不妨也试试解耦,或许会收到意想不到的惊喜。
更多精彩内容欢迎关注BUGly的微信公众账号:
腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS,AndroID 官方 *** 作系统,鹅厂的工程师都在使用,快来加入我们吧!
总结以上是内存溢出为你收集整理的【腾讯bugly干货分享】解耦---Hybrid H5跨平台性思考全部内容,希望文章能够帮你解决【腾讯bugly干货分享】解耦---Hybrid H5跨平台性思考所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)