解耦——Hybrid H5跨平臺性思考
作者:徐嘉偉(garyjwxu),2013年畢業後加入騰訊,曾先後主導負責財付通主站生活類板塊、微信信用卡還款、H5支付中心、手Q AA收款、手Q紅包、QQ錢包商戶平臺的前端開發工作,現為FIT金融市場部前端開發工程師。
本文選自《程式設計師》,更多精彩文章請訂閱2016年《程式設計師》。
跨平臺,是HTML5最重要的能力之一。而Hybrid H5因強依賴於具體App,往往不具有跨平臺性。這時,將強依賴關係解耦,即可恢復HTML5的跨平臺能力。近期我負責手Q紅包打賞專案的前端開發,因專案涉及到多App跨平臺相容,對Hybrid H5的跨平臺性有了一定的感悟和思考。在這裡做下總結分享,希望能對大家有所收穫。
Hybrid H5跨平臺性
進入正題之前,先解釋下本文主題的兩個名詞。
- Hybrid H5,即混合了原生能力的HTML5。區別於純粹Web端的HTML5,它可呼叫原生的能力,強依賴於具體原生App,與原生共同構建整個App的UI層,是App UI層很好的靈活性補充。微信和手Q上的HTML5業務一般都屬於Hybrid H5的範疇。
- 跨平臺性,即一個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 //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原有跳轉邏輯處理。以第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的靈活性等等。遇到這種情況大家不妨也試試解耦,或許會收到意想不到的驚喜。
訂閱諮詢:
• 線上諮詢(QQ):2251809102
• 電話諮詢:010-64351436
• 更多訊息,歡迎關注“程式設計師編輯部”
相關推薦
解耦——Hybrid H5跨平臺性思考
作者:徐嘉偉(garyjwxu),2013年畢業後加入騰訊,曾先後主導負責財付通主站生活類板塊、微信信用卡還款、H5支付中心、手Q AA收款、手Q紅包、QQ錢包商戶平臺的前端開發工作,現為FIT金融市場部前端開發工程師。 本文選自《程式設計師》,更多精彩文
【騰訊bugly乾貨分享】解耦---Hybrid H5跨平臺性思考
跨平臺,是H5最重要的能力之一。而 Hybrid H5 因強依賴於具體 app,往往不具有跨平臺性。這時,將強依賴關係解耦,即可恢復 H5 的跨平臺能力。近期本人負責 手Q 紅包打賞專案的前端開發,因專案涉及到多 app 跨平臺相容,對 hybrid H5
關於解耦方式的思考
解耦都是需要代理的。本質上並不存在沒有代理就發生兩個部件之間解耦的情況。 耦合,指的是兩個可以協作的部件的關係。 A和B可以協作,則A和B的關係是耦合。 如果A可以和O,P,Q,S...(簡稱集合F)協作,則A就和集合F發生了耦合,如果A發生了變化,想要維持系統正常,那麼集合F就需要順應A的
移動H5前端性能優化指南
例如 coo forms 指南 touchend meta 大於 動畫 節點 移動H5前端性能優化指南 概述 1. PC優化手段在Mobile側同樣適用2. 在Mobile側我們提出三秒種渲染完成首屏指標3. 基於第二點,首屏加載3秒完成或使用Loading4. 基於聯通
簡單的工廠+反射+ xml解耦和
logs trace att loader 類加載 一個 encoding saxreader cnblogs 傳統的調用業務層是: CustomerServiceImpl csi = new CustomerServiceImpl(); 通過面向接口編程改進過後:
iOS 運行時RunTime使用場景一:打點統計用戶行為,深度解耦
cab else 地址 註入 響應事件 加載失敗 tor top perf 轉自:http://www.jianshu.com/p/0497afdad36d 用戶統計.jpeg 用戶行為統計(User Behavior Statistics, UBS)一直是移
Android學習探索之運用MVP設計模式實現項目解耦
Android 前言: 一直致力於提高開發效率降低項目耦合,今天想抽空學習一下MVP架構設計模式,學習一下如何運用到項目中。 MVP架構設計模式 MVP模式是一種架構設計模式,也是一種經典的界面模式。MVP中的M代表Model, V是View, P是Pre
ELK日誌平臺----解耦配置文件
elk日誌分析平臺本文記錄了三個配置文件:第一個:all.conf 通過一個配置文件,配置輸入輸出,實例;第二個:shipper.conf配置logstash收集日誌內容到redis裏;第三個:indexer.conf配置logstash從redis裏讀取日誌內容輸出到Elasticsearch裏。第二個跟第
commons-logging日誌實現解耦
truct illegal 工廠 man int 行為 efault context buffered 一、需要解耦 日誌是實際應用中的一個重要部分,日誌系統也有許多開源的實現,如java.util.logging, logback, log4j系列等。
yarn架構——本質上是在做解耦 將資源分配和應用程序狀態監控兩個功能職責分離為RM和AM
沒有 占用 業界 imageview 技術分享 其他 而是 基本 mapreduce Hadoop YARN架構解讀 原Mapreduce架構 原理架構圖如下: 圖 1.Hadoop 原 MapReduce 架構 原 MapReduce 程序的流程:首先用戶程
依賴耦合、解耦、控制反轉(IOC)、依賴註入
增加 clas 說明 class a lan xxx ron pen pub 隨著net的深入學習,出現了很多概念性的東西需要理解,現在統一記錄一下。 1.依賴:現階段在任何一個有請求作用的系統,都會出現A類調用B類的情況,這時候A類就依賴於B類,A類和B類存在依賴關系。
[轉]使用rosbridge協議實現安卓跟ros的解耦
團隊 機械臂 實現 能力 比較 content ice 出了 .org 安卓與ROS通信的現狀 因為ROS官方支持的語音綁定只有C++和Python,所以目前安卓想與ROS通信,必須借助半官方的rosjava包,而Rosjava太重了,因為它跟C++/Python一樣,是
Javaweb項目開發的前後端解耦的必要性
推薦 city 除了 app weblogic 兼容性 sdn servle 事務 JavaWeb項目為何我們要放棄jsp?為何要前後端解耦?為何要動靜分離? 使用jsp的痛點: 1.jsp上動態資源和靜態資源全部耦合在一起,服務器壓力大,因為服務器會收到各種靜態
java跨平臺性
源碼 語言 註意 橋梁 生成 機器語言 直接 字節碼 java代碼 Java跨平臺是怎樣實現的呢?這就要談及Java虛擬機(Java Virtual Machine,簡稱 JVM)。 我們編寫的Java源碼,編譯後會生成一種 .class 文件,稱為字節碼文件。 字節碼不能
window.open()詳解及瀏覽器兼容性問題示例探討
ati 一個 span 如何 用戶 兼容性問題 說明 aid ask 這篇文章主要介紹了window.open()的使用及瀏覽器兼容性問題方面的知識,感興趣的朋友可以參考下 一、基本語法: window.open(pageURL,name,parameters) 其中:
Struts2 第五講 -- Struts2與Servlet的API解耦
ces user gets namespace jsp throw 方法 ioc 取數據 為了避免與 Servlet API 耦合在一起, 方便 Action 做單元測試, Struts2 對 HttpServletRequest, HttpSession 和 Serv
spring中的Ioc技術是怎樣實現解耦的 原文地址 : http://blog.csdn.net/liang5603/article/details/52002994
ioc容器 可能 深入 修改 知識 動態 出現 工廠方法 邏輯 1. IoC理論的背景我們都知道,在采用面向對象方法設計的軟件系統中,它的底層實現都是由N個對象組成的,所有的對象通過彼此的合作,最終實現系統的業務邏輯。圖1:軟件系統中耦合的對象如果我們打開機械式手表的後蓋,
案例40-層與層之間的解耦(面向接口編程)
sql 獲得 post turn 對象 轉發 詳情 hot axon 1 bean.xml配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans> <!-- 配置AdminServ
委托(作用:解耦)
AR 輸入 實例 wrong console nbsp sum tasks with 1.了解委托 MyDelegate類代碼如下: using System; using System.Collections.Generic; using System.Linq;
什麽是跨平臺性?原理是什麽?JVM
程序 所有 pan 多個 VM 配置 直接 jvm spa 所謂跨平臺性,是指java語言編寫的程序,一次編譯後,可以在多個系統平臺上運行。 實現原理:Java程序是通過java虛擬機在系統平臺上運行的,只要該系統可以安裝相應的java虛擬機,該系統就可以運行java