1. 程式人生 > >解耦——Hybrid H5跨平臺性思考

解耦——Hybrid H5跨平臺性思考

作者:徐嘉偉(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 //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