標籤:class blog code http tar com
搜尋解決方案的時候找到了Rob Napier 的博文:Drop-in offline caching for UIWebView (and NSURLProtocol) 文章介紹了使用NSURLProtocol實現UIWebView的離線緩衝的簡單實現,你可以在github上下載這個demo的代碼。
rob認為無論是“MKNetworkKit”還是”AFCache”實現的緩衝都過於複雜,而他想要的是一個簡單機制:
1、你使用了UIWebView指向來顯示一個有映像嵌入的網站。
2、當你的裝置online時,你有正常的緩衝演算法。
3、當你的裝置offline時,你可以顯示頁面的最後一個版本。
這個demo裡做了一個很簡單的測試:將cnn.com運行一次,然後再將網路斷掉,去瀏覽這些資料。
現有解決方案:
Matt Gallagher 有一些有趣的想法,使用NSURLCache的子類來實現,但是Rob發現這是不可靠的,尤其是iOS5的HTTP緩衝規則十分複雜,在許多情況下如果你不訪問伺服器便不能獲知你緩衝的資料是否有效。另外,一些必要的素材如果沒有被緩衝,那麼在離線時前期做的緩衝工作就實效了。(輝:NSURLCache實現離線閱讀的一點小心得 我也曾討論了一些相關問題)
AFCache也被認為是一個很好的解決方案(輝:有時間我會對這個開源庫進行詳細評估,表面上看就是connection、NSURLCache、NSURLProtocol的綜合解決方案)。短時間內作者並沒有使測試通過,但是AFCache的作者也在文章後邊回複說,採納了Rob的想法,已經提交代碼到github上。
要點:
1、儘早註冊你的URLProtocol(application:didFinishLaunchingWithOptions:)。
2、NSURLProtocol是NSURLConnection的handler。NSURLConnection的每個請求都會去便利所有的Protocols,並詢問你能處理這個請求麼(canInitWithRequest: )。如果這個Protocol返回YES,則第一個返回YES的Protocol會來處理這個connection。Protocols的遍曆是反向的,也就是最後註冊的Protocol會被優先判斷。
3、 當你的handler被選中了,connection就會調用–> initWithRequest:cachedResponse:client:,緊接著會調用–>startLoading。然後你需要負責回調:–>URLProtocol:didReceiveResponse:cacheStoragePolicy:,有些則會調用:–>URLProtocol:didLoadData:, 並且最終會調用–>URLProtocolDidFinishLoading:。你有沒有發現這些方法和NSURLConnection delegate的方法非常類似——這絕非偶然!
4、當online的情況下,RNCachingURLProtocol只是負責將請求轉寄給一個新的NSURLConnection,並且拷貝一份結果給原來的connection。offline時, RNCachingURLProtocol就會從磁碟裡載入先前的結果,並將這些資料發回給串連。整個過程只有區區200行代碼(不包含Reachability)。
5、這裡還有一個有趣的問題,就是當RNCachingURLProtocol建立了一個新的NSURLConnection的,即新的connection也會去找一個handler。 如果RNCachingURLProtocol說可以處理,那麼就死迴圈了。怎麼解決呢?通過添加自訂HTTP Header(X-RNCache)來標記這個請求,告訴RNCachingURLProtocol不要再處理這個請求。
6、它可以響應所有的connection,所以你可能需要修改canInitWithRequest:來 選擇你要緩衝的資料。
另外:並發請求或複雜網路請求的緩衝請使用MKNetworkKit(我們也在一個項目中使用了這個類庫,非常輕量快捷是ASI的很不錯的替代品)。
總結一下:
這項技術不是用來替代AFCache、MKNetworkKit的,他只是用來解決獨立的、簡單問題的(當然它也可以通過複雜實現來解決複雜問題)。 NSURLProtocol是非常強大的,Rob已經使用它來監聽網路流量(如PandoraBoy中的幾個ProxyURLProtocol類)。它非常值得你將其添加到你的工具箱中。
執行個體代碼下載:https://github.com/rnapier/RNCachingURLProtocol
參見demo中的類檔案:RNCachingURLProtocol.m。
一定要看Nick Dowell在評論中回複的對於redirect的解決辦法:(Code to fix HTTP redirect handling: https://gist.github.com/1885821)
| 1234567891011121314 |
(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response {if ([response isKindOfClass:[NSHTTPURLResponse class]]){NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;if ([HTTPResponse statusCode] == 301 || [HTTPResponse statusCode] == 302){NSMutableURLRequest *mutableRequest = [request mutableCopy];[mutableRequest setURL:[NSURL URLWithString:[[HTTPResponse allHeaderFields] objectForKey:@”Location”]]];request = [mutableRequest copy];[[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];}}return request;} |