一個(gè)Hybrid框架的搭建思路

不論iOS還是Android,都有瀏覽器,對(duì)應(yīng)到開(kāi)發(fā)中有個(gè)控件叫做UIWebView,在App開(kāi)發(fā)中摒棄原生頁(yè)面編寫(xiě),而在App中嵌入U(xiǎn)IWebView,使用開(kāi)發(fā)網(wǎng)頁(yè)的方式開(kāi)發(fā)App,這就是所謂的Hybrid開(kāi)發(fā)吧。
這種開(kāi)發(fā)模式最大的好處是資源在服務(wù)端,迭代速度快,不需要頻繁提交審核。缺點(diǎn)是:
1.由于HTML等網(wǎng)頁(yè)資源在服務(wù)端,所以加載速度有點(diǎn)慢,用戶體驗(yàn)欠佳
2.HTML的能力有限,不能或者有限的能調(diào)用諸如定位,藍(lán)牙,支付等功能
3.UIWebView在各個(gè)系統(tǒng)版本上的兼容性問(wèn)題
圍繞上面的這幾個(gè)問(wèn)題,我們做了如下事情

網(wǎng)頁(yè)資源提前加載到本地

在App啟動(dòng)時(shí)會(huì)請(qǐng)求一個(gè)配置文件,包含版本號(hào)和包地址,舉例我們的文件如下:

{"errorMsg": "", "code": 0, "data": [{"zipDownloadUrl": "https://192.168.10.10/cdn/updatepkg/matrix_v2.2.606.zip", "version": "2.2.606", "packageName": "dist"}]}

這個(gè)配置文件每次啟動(dòng)會(huì)去下載,并比對(duì)version跟本地的version是否一致,不一致則下載,一致則略過(guò)。

部分對(duì)用戶體驗(yàn)要求較高的頁(yè)面使用原生開(kāi)發(fā)

對(duì)用戶體驗(yàn)要求較高的頁(yè)面(比如首頁(yè)),我們最好還是使用原生開(kāi)發(fā)。這也是Hybrid的真正意義所在吧。但隨之也會(huì)產(chǎn)生一個(gè)新的問(wèn)題:原生頁(yè)面和H5的跳轉(zhuǎn)實(shí)現(xiàn)。
原生->H5,只需要進(jìn)入一個(gè)帶UIWebView的原生頁(yè)面即可
H5->原生,這個(gè)有點(diǎn)復(fù)雜,需要定義一套協(xié)議用于頁(yè)面跳轉(zhuǎn)。
網(wǎng)上的參考文檔不少,比較有名的是葉小釵的博客:http://www.cnblogs.com/yexiaochai/p/4921635.html
這里我在添加一些我們團(tuán)隊(duì)總結(jié)的吧
<font color="red">1.http和https的處理</font>
由于現(xiàn)在對(duì)網(wǎng)絡(luò)安全的重視(當(dāng)然蘋(píng)果也由于要求),越來(lái)越多的App加入了https的支持。有一種非常普遍的問(wèn)題需要我們注意:跨域。舉個(gè)例子,我們的UIWebView加載的頁(yè)面是https://www.baidu.com,但baidu.com這個(gè)頁(yè)面中的某段js代碼會(huì)執(zhí)行頁(yè)面跳轉(zhuǎn),而跳轉(zhuǎn)的協(xié)議是hybrid,也就是諸如hybrid://AViewController類(lèi)似的URL地址。App意識(shí)到這可能是一段不安全的代碼,因此不給于執(zhí)行,之前就是遇到了這個(gè)問(wèn)題導(dǎo)致我們App不能響應(yīng)任何【H5->原生】跳轉(zhuǎn)。那如何“魚(yú)和熊掌兼得”呢,解決方案有兩種,各有利弊:
在調(diào)用UIWebView的loadRequest方法時(shí),先去判斷本地有沒(méi)有資源,有的話加載本地資源,這樣其實(shí)就變相的走了http協(xié)議或者file協(xié)議,就不存在https跨域的問(wèn)題。但是對(duì)于UIWebView的js請(qǐng)求我們還是要進(jìn)行攔截,走本地的https請(qǐng)求即可。這個(gè)方案的優(yōu)點(diǎn)在于,由于html文件資源在本地所以加載資源速度比較快,用戶體驗(yàn)不錯(cuò);缺點(diǎn)就是我們必須通過(guò)URLProtocol重新定義所有的請(qǐng)求,這就需要我們?cè)赗equest請(qǐng)求的時(shí)候避免在httpbody或者h(yuǎn)ttpbodystream中設(shè)置參數(shù),轉(zhuǎn)而在header中設(shè)置。另外一種解決方案是,請(qǐng)求的時(shí)候?qū)RL地址直接從https替換成http,這樣的好處就是不需要定義所有的網(wǎng)絡(luò)請(qǐng)求,但由于html文件資源在遠(yuǎn)端所以加載資源速度比較慢。
<font color="red">2.參數(shù)的傳遞</font>
在原生和H5之間跳轉(zhuǎn)的時(shí)候,參數(shù)傳遞都作為URL地址的一部分,例如AViewController中有屬性u(píng)sername和tel,那我們?cè)谔D(zhuǎn)到AViewController是路由的寫(xiě)法應(yīng)該是類(lèi)似:

    hybrid://AViewController?username=123&tel=1526181629x

這個(gè)很好理解,但有種情況,如果接受的是個(gè)url,并且url中也有參數(shù)怎么辦?(啥啥啥?黑人問(wèn)號(hào)臉。。。)舉例

    hybrid://AViewController?url=http://www.baidu.com?username=123&tel=1526181629x

那請(qǐng)問(wèn),tel是AViewController的參數(shù)還是url自帶的參數(shù)?這是個(gè)問(wèn)題。解決方案其實(shí)也很簡(jiǎn)單:一層層encode,比如有一個(gè)url那就encode一次,如果url中還帶url那再encode一次。decode的時(shí)候也是一樣,一層一層decode。

使用原生SDK編寫(xiě)一套可供js調(diào)用的API

路由除了可以用于原生頁(yè)面和H5頁(yè)面跳轉(zhuǎn),還可以用于調(diào)用本地的一些功能(或者稱(chēng)“API”或者 “服務(wù)”,是的,我習(xí)慣稱(chēng)“服務(wù)”這種說(shuō)法),下面是我們App預(yù)設(shè)的一些服務(wù):

// 打開(kāi)新頁(yè)面
#define SERVICE_OPENNEWPAGE                 @"hybrid://openNewPage?param="
// 更新導(dǎo)航欄
#define SERVICE_UPDATEUIHEADER              @"hybrid://updateNavigationBar?param="
// 回退
#define SERVICE_PAGEBACK                    @"hybrid://back?param="
// 獲取定位
#define SERVICE_GETLOCATION                 @"hybrid://getLocation?param="
// 獲取網(wǎng)絡(luò)類(lèi)型,狀態(tài)
#define SERVICE_NETWORKTYPE                 @"hybrid://getNetWorkType?param="

因此只要在html中指明如上的鏈接即可。有時(shí)我們需要在調(diào)用服務(wù)成功后給個(gè)反饋,那就需要在native調(diào)H5的方法,幸好JSContext幫我們實(shí)現(xiàn)了這個(gè)想法。

應(yīng)對(duì)UIWebView在各個(gè)系統(tǒng)版本上的兼容性問(wèn)題

這個(gè)問(wèn)題基本是無(wú)解的:在iOS9.1中,如果調(diào)用UIWebView的goback方法是不起作用的,是的,很遺憾這是UIWebView的一個(gè)Bug,因此盡量少的在一個(gè)頁(yè)面中進(jìn)行html的跳轉(zhuǎn)是唯一的解決方案。如果你夠棒,可以將一些開(kāi)源的瀏覽器內(nèi)核寫(xiě)進(jìn)你的App,除此之外你毫無(wú)辦法。

好了,基本的理論知識(shí)已經(jīng)完了,框架的代碼可以在這里獲?。?br> https://github.com/zjh171/Watermelon
下面我們來(lái)看下我們的這個(gè)西瓜框架。

如圖,當(dāng)一個(gè)Request 發(fā)起后(這個(gè)Request 可以是UIWebView中的某個(gè)鏈接點(diǎn)擊產(chǎn)生,也可以UIWebView的Ajax請(qǐng)求),通過(guò)Watermelon 進(jìn)行攔截,如果是正常的HTTP請(qǐng)求則放過(guò),如果是我們定義的scheme則單獨(dú)做處理。OHHttpStub是基于NSURLProtocol實(shí)現(xiàn)的URL Request 攔截系統(tǒng)。方法

+(id<OHHTTPStubsDescriptor>)stubRequestsPassingTest:(OHHTTPStubsTestBlock)testBlock
                                   withStubResponse:(OHHTTPStubsResponseBlock)responseBlock;

而參數(shù)testBlock就是需要攔截的Request請(qǐng)求,攔截后我們可以自己實(shí)現(xiàn)自己的Request請(qǐng)求,并返回?cái)?shù)據(jù)給responseBlock。至于這里為什么使用OHHttpStub不使用原生的NSURLProtocol是為了避免與其他的使用NSURLProtocol 的三方庫(kù)(比如Bulgly)沖突。當(dāng)攔截成功后,我們?cè)侔呀Y(jié)果分發(fā)給WebView。

WebView接受到數(shù)據(jù)就解析Request請(qǐng)求中的數(shù)據(jù),并做相應(yīng)處理。大概的流程就是這樣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容