如何打造一個高性能Hybrid App

xpsu3540 7年前發布 | 10K 次閱讀 高性能 移動開發

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

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

方案選擇

從這個點出發,我們需要考慮,以怎樣的形式來提前拿到資源包(css,js,html,通用的圖片等),減少這些靜態資源的網絡請求,增加加載速度。無非就是一下兩點:

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

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

單純從業務層來說,如果你的業務夠簡單,其實第一種方式已經完全滿足,每次需要新增頁面就重新發版嘛,雖然顯得有點愚笨,但是還是能滿足的。

但是從長遠的角度來說,我們要做到盡可能的動態化,動態化是客戶端的熱點,我們要做到盡量不依賴于版本更新來實現動態化。對于iOS來說,更新機制本身就非常緩慢,要通過app store的審核有時候還需要靠人品,更何況用戶也不一定買賬,他們不一定會更新我們的app。在這樣的情況下,第二種方案就會顯得更加友好。

設計加載流程

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

抽出細節,大體上可以歸結為下圖所示的結構圖:

app與服務端交互

我來解釋下這個圖,我是建立在客戶端已經實現socket層協議,所以能與server保持長連接以至于server能主動push數據的情況,實現這種協議蠻復雜的。實際上如果沒有這個協議,那就需要client找時機主動去server請求(app啟動時請求一次?或者是每隔一段時間請求一次,取決于你),本文以后者為例。

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

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

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

3.server根據config里的選項,比對從client拿到的config,發現客戶端是舊版本的config,OK,則下發新的config給client,并且發送從database里拿到的資源包(為了加快速度,可以部署到CDN)。

4.client拿到最新的資源包后,在本地進行解密,解壓等操作,并映射成對應ULR相對于本地的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的子類

這里只介紹與我們需求相關的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已經全部介紹完畢。

附上前后加載速度對比:

加載速度對比

 

 

來自:http://www.jianshu.com/p/bf14ab437feb

 

 本文由用戶 xpsu3540 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!