如何打造一個高性能的Hybrid App

 

引言

在多元化的今天,一個熱門的移動app,或多或少都會有內在H5在其中。而對於一個有不少運營場景的app來講,這種狀況更常見了。試想一下,若是在一個公司,存在不少native和H5同時須要開發的頁面,爲了節省開發成本,此時若是隻開發H5,就須要考慮native的體驗了,而這就是本文的目的,如何讓native端擁有像加載本地頁面同樣的速度去加載H5。css

在app內加載H5速度慢一直是客戶端開發的痛點,拋開H5的體驗自己與native就有差距不說,若是加載速度還很慢,這將會對用戶體驗形成巨大影響。那麼像作到像native頁面同樣瞬間加載完H5,思路就會變得比較清晰了:提早在本地存儲遠程資源包html

方案選擇

從這個點出發,咱們須要考慮,以怎樣的形式來提早拿到資源包(css,js,html,通用的圖片等),減小這些靜態資源的網絡請求,增長加載速度。有如下兩種方案:web

1.將資源包在app打包階段直接植入網絡

2.在運行時動態下載資源包app

單純從業務層來講,若是你的業務夠簡單,其實第一種方式已經徹底知足,每次須要新增頁面就從新發版嘛,雖然顯得有點愚笨,可是仍是能知足的。socket

可是從長遠的角度來講,咱們要作到儘量的動態化,動態化是客戶端的熱點,咱們要作到儘可能不依賴於版本更新來實現動態化。對於iOS來講,更新機制自己就很是緩慢,要經過app store的審覈有時候還須要靠人品,更況且用戶也不必定買帳,他們不必定會更新咱們的app。在這樣的狀況下,第二種方案就會顯得更加友好和方便快捷。url

設計加載流程

那麼,該怎麼設計一套完整的解決方案來知足運行時動態下載資源包呢?spa

抽出細節,大致上能夠歸結爲下圖所示的結構圖:設計

我來解釋下這個圖,我是創建在客戶端已經實現socket層協議,因此能與server保持長鏈接以致於server能主動push數據的狀況,實現這種協議蠻複雜的。實際上若是沒有這個協議,那就須要client找時機主動去server請求(app啓動時請求一次?或者是每隔一段時間請求一次,取決於你),本文之後者爲例。code

下面我來演示下一個完整的下載新資源包的過程:

1.運營小妹以爲某節日要到了,須要發佈一個新的頁面,而後在運營後臺生成資源包,運營後臺會自動更新config,其中包括資源包的version,是否強制關閉加載本地資源包(降級策略,防止這個組件自己有BUG),還有一些hotpatch腳本。而且將資源包根據裏面的內容部署到remote database

2.在合適的時機,client發起http請求向server查詢是否有新版本的資源包,並帶上本地的config

3.server根據config裏的選項,比對從client拿到的config,發現客戶端是舊版本的config,OK,則下發新的configclient,而且發送從database裏拿到的資源包(爲了加快速度,能夠部署到CDN)。

4.client拿到最新的資源包後,在本地進行解密,解壓等操做,並映射成對應URL相對於本地的local file url。好比:http://www.baidu.com 這個網址下的靜態資源文件在本地的 file://dsalkfjsldfjalsd/ 目錄下。

至此,已經完成資源包的下載。

攔截並加載本地資源包

那麼有了資源包後,怎麼能讓app像native頁面的速度去加載H5呢。

其實原理就是對H5請求進行攔截,若是本地已經有對應的靜態資源文件,則直接加載,這樣就能達到「秒開」webview的效果。

對於iOS而言,這就須要用到NSURLProtocol這個神器了。接下來,分析下它究竟是什麼東西,咱們怎麼利用它達到上述效果。

NSURLProtocol:它可以讓你去從新定義蘋果的URL加載系統(URL Loading System)的行爲,URL Loading System裏有許多類用於處理URL請求,好比NSURL,NSURLRequest,NSURLConnection和NSURLSession等。當URL Loading System使用NSURLRequest去獲取資源的時候,它會建立一個NSURLProtocol子類的實例,你不該該直接實例化一個NSURLProtocol,NSURLProtocol看起來像是一個協議,但其實這是一個 類,並且必須使用該類的子類,而且須要被註冊。

換句話說,NSURLProtocol能攔截全部當前app下的網絡請求,而且能自定義地進行處理。

代碼以下

這裏只介紹與咱們需求相關的NSURLProtocol方法。

搞了這麼多,其實最核心的就是前四個方法:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request

這個方法的做用是判斷當前protocol是否要對這個request進行處理(全部的網絡請求都會走到這裏,因此咱們只須要對咱們產生的request進行處理便可)。

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request

這個方法其實很強大,它能夠對request進行預處理,好比對header加一些東西什麼的,咱們這裏沒什麼要改的,因此直接返回request就行了。

- (void)startLoading

重點是這個方法,咱們這裏須要作一件事,就是本身拼裝httpResponse,而且返回給url load system,而後到了webview那一層,會收到response,對於webview而言,加載本地和走網絡拿到的response是徹底同樣的。因此上述代碼展現瞭如何拼裝一個httpResponse,當組裝完成後,須要調用self.client將數據傳出去。

何爲self.client,這個東西其實就是protocol與url load system交互的一個對象,系統提供給咱們的,這樣理解就夠了。

須要注意的是,細心的讀者會看到else裏會有一段代碼:

[NSURLProtocol setProperty:@YES forKey:WDHybridResourceProtocolHandledKey inRequest:newRequest];

這個是幹什麼用的?else的做用是當本地不存在這個文件時,則主動從新發請求,此時又會調用canInitWithRequest,若是不設置flag,則會無限遞歸了。因此你懂得。

固然,從新發請求天然要實現NSURLConnectionDelegate

總結

至此,如何快速加載H5已經所有介紹完畢。

附上先後加載速度對比:

相關文章
相關標籤/搜索