讓UIWebview擁有超強(qiáng)的圖片處理能力

首先,最近有個想法是讓UIWebview支持webp铲掐,那么原生的webview引擎是不支持的譬猫,所以就有了如標(biāo)題所寫的想法讯檐。其實不僅僅局限于為了讓其支持webp,如果能讓webview的圖片請求都走向自己實現(xiàn)的圖片庫删窒,那么可以實現(xiàn)很多很多黑科技(比如每個圖片加個app特定水恿芽选?或者說裁剪圖片使webview能有更好的性能表現(xiàn)肌索?很多很多的功能蕉拢,不一一列舉了)。

關(guān)于緩存

在開始講解之前诚亚,我覺得有必要先普及一下 NSURLRequest cache 相關(guān)知識晕换。
  • webview和普通的NSURLRequest都是用同一套cache策略么,也就是說他們的cache共享嗎站宗?
  • 具體的cache策略是怎樣的闸准,是怎樣產(chǎn)生cache的?如果webview請求過一個圖片梢灭,而在webview外再次請求同一個圖片夷家,會用直接用cache么蒸其?
  • 系統(tǒng)原生提供的cache的穩(wěn)定性茵汰?
帶著這幾個問題番捂,我做了大量的實驗,這里直接給出結(jié)果庇忌,因為這不是本文的重點义屏。
  1. 兩者的確是公用cache的靠汁。
  2. 在實驗中,webview請求過一個圖片之后闽铐,用NSURLRequest請求同一個圖片會產(chǎn)生兩個結(jié)果蝶怔。
    在前者請求完成后的短時間內(nèi)直接進(jìn)行下次請求,會重新請求而不會拿cache兄墅,而過一段時間后請求踢星,則會拿cache,個人猜測是處理cache需要時間隙咸。
  3. 關(guān)于系統(tǒng)提供的cache斩狱,os隨時有可能清空你的app cache,這是不可靠的扎瓶!這也是為什么SDWebImage默認(rèn)會有自己的一套緩存策略的原因。

上述說的cache策略是建立在你正確理解并且用好的NSURLRequestCachePolicy的前提下的泌枪。為什么這么說呢概荷?有些人會濫用NSURLRequestCachePolicy ,比如明明NSURLRequestReloadIgnoringLocalCacheData碌燕,卻說怎么沒緩存误证,這就有點蛋疼了。關(guān)于cache策略可以看看官方文檔修壕,比我說的清楚多了~

總結(jié)下:

無論采用哪種NSURLRequestCachePolicy愈捅,在請求的時候都會cache。而在下次請求(不包括[webview reload]慈鸠,刷新會強(qiáng)制重新發(fā)請求)的時候蓝谨,則會根據(jù)NSURLRequestCachePolicy的策略決定是否拿緩存。如果是NSURLRequestUseProtocolCachePolicy青团,還要根據(jù)對應(yīng)的協(xié)議(http\https\ftp)的服務(wù)端提供的response來決定譬巫。

OK 緩存講完,我們來看重點督笆。

其實思路挺簡單的芦昔,在之前介紹hybrid app的時候,已經(jīng)介紹了NSURLProtocol這個神器的作用娃肿。這里其實也是用NSURLProtocol來達(dá)到目的咕缎。
想一想珠十,無非就是攔截掉webview的請求,將本身在webview的URL Load System的請求抽出來凭豪,我們自己處理掉焙蹭,然后組裝成response返回給webview。對于webview而言是完全透明的墅诡,它不關(guān)心中途的網(wǎng)絡(luò)請求是如何完成的壳嚎,它只知道它發(fā)出了請求,并且收到response即可末早。
換句話說即我們自己處理整個請求的所有過程烟馅。
如下圖所示:

流程圖

簡簡單單看一下原理是很簡單,但是就是因為這小小的動作我們可以做到很多原本沒法讓webview做到的事情然磷。

  • 支持webp
  • 實現(xiàn)與自己實現(xiàn)的圖片庫一致的緩存策略
  • 將圖片大小尺寸的URL拼接策略放在本地

支持webp:將圖片請求在自己實現(xiàn)的圖片庫中完成郑趁,然后轉(zhuǎn)成webview支持的data格式透傳回去即可。

緩存:我們都知道系統(tǒng)的緩存是不可靠的姿搜,很多開發(fā)者會采用自己的緩存策略寡润,而webview的請求如果不進(jìn)行干預(yù)是沒法做到走我們自己的緩存策略的,但是如文章所說舅柜,即可做到梭纹。

圖片尺寸優(yōu)化:往往對于H5而言,如果前端開發(fā)者沒有注意圖片尺寸問題致份,在PC和手機(jī)上圖片都采用一個尺寸的話变抽,這是十分不好的,手機(jī)屏幕小并不需要這么大尺寸的圖氮块。往往大多數(shù)的圖片CDN都是會支持裁剪的绍载,而攔截圖片請求在URL上拼接size是一個解決方式。

還有許許多多的功能滔蝉,比如開頭提到的水印击儡,從我這個app打開的圖片都被我加上了本app的icon,是不是很屌蝠引。

這里有個小插曲:在擼demo的時候阳谍,我本想讓demo中的SDWebImage請求同一個圖片,結(jié)果發(fā)現(xiàn)怎么也走不到protocol中立肘。搞了半天沒法只能去看文檔边坤,發(fā)現(xiàn)如下:

NSURLSession

NSURLSession與NSURLConnection注冊NSURLProtocol的方式是不同的!

demo下載地址

代碼演示

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    NSString *path = request.URL.path;
    // 只處理是圖片的URL請求
    if ([path hasSuffix:@".jpg"] || [path hasSuffix:@".jpeg"] || [path hasSuffix:@".webp"]) {
        if ([NSURLProtocol propertyForKey:WebviewImageProtocolHandledKey inRequest:request]) {
            return NO;
        }
        return YES;
    }
    return NO;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

- (void)startLoading {
    // 將本地的緩存組裝成報文傳回去
    NSData *data = [self imageDataWithURL:self.request.URL];
    
    if (data) {
        NSString *mimeType = @"image/jpeg";
        NSMutableDictionary *header = [[NSMutableDictionary alloc] initWithCapacity:2];
        NSString *contentType = [mimeType stringByAppendingString:@";charset=UTF-8"];
        header[@"Content-Type"] = contentType;
        header[@"Content-Length"] = [NSString stringWithFormat:@"%lu", (unsigned long) data.length];
        
        NSHTTPURLResponse *httpResponse = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
                                                                      statusCode:200
                                                                     HTTPVersion:@"1.1"
                                                                    headerFields:header];
        
        [self.client URLProtocol:self didReceiveResponse:httpResponse cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didLoadData:data];
        [self.client URLProtocolDidFinishLoading:self];
    } else {
        // 這里其實應(yīng)該是圖片庫對于圖片請求的處理谅年,每個人實現(xiàn)的圖片庫不一樣茧痒,所以我就直接以webview的方式請求了
        NSMutableURLRequest *newRequest = [self.request mutableCopy];
        newRequest.allHTTPHeaderFields = self.request.allHTTPHeaderFields;
        [NSURLProtocol setProperty:@YES forKey:WebviewImageProtocolHandledKey inRequest:newRequest];
        
        self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }
}

- (void)stopLoading {
    [self.connection cancel];
}

- (NSData*)imageDataWithURL:(NSURL*)url {
    // 假裝這是一個緩存,這里拋磚引玉下融蹂,大家可以自己實現(xiàn)自己的圖片緩存策略
    if ([url.absoluteString isEqualToString:@"http://kuailejim.com/images/background-cover.jpg"]) {
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"background-cover" ofType:@"jpg"];
        NSData *data = [NSData dataWithContentsOfFile:filePath];
        return  data;
    }
    return nil;
}

由于每個開發(fā)團(tuán)隊可能有自己的圖片處理庫旺订,所以我這里就只給出簡單的思路弄企,大家可以自行按照自己的圖片庫替代上述有注釋的代碼。

成果演示

demo
處理后的抓包
處理前的抓包

可以很明顯的看出区拳,圖片請求沒了拘领,因為走了本地的緩存。

最后

本文只給出了如何進(jìn)行網(wǎng)絡(luò)請求替換樱调,并且給出一個小demo约素,其實能做的遠(yuǎn)遠(yuǎn)不止這些。正如文中所說webp笆凌,圖片尺寸優(yōu)化等等圣猎,如果你感興趣也可以自己擼一個demo玩玩~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市乞而,隨后出現(xiàn)的幾起案子送悔,更是在濱河造成了極大的恐慌,老刑警劉巖爪模,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欠啤,死亡現(xiàn)場離奇詭異,居然都是意外死亡屋灌,警方通過查閱死者的電腦和手機(jī)洁段,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來共郭,“玉大人眉撵,你說我怎么就攤上這事÷渌埽” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵罐韩,是天一觀的道長憾赁。 經(jīng)常有香客問我,道長散吵,這世上最難降的妖魔是什么龙考? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮矾睦,結(jié)果婚禮上晦款,老公的妹妹穿的比我還像新娘。我一直安慰自己枚冗,他們只是感情好缓溅,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赁温,像睡著了一般坛怪。 火紅的嫁衣襯著肌膚如雪淤齐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天袜匿,我揣著相機(jī)與錄音更啄,去河邊找鬼。 笑死居灯,一個胖子當(dāng)著我的面吹牛祭务,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怪嫌,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼义锥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了喇勋?” 一聲冷哼從身側(cè)響起缨该,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎川背,沒想到半個月后贰拿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡熄云,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年膨更,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缴允。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡荚守,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出练般,到底是詐尸還是另有隱情矗漾,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布薄料,位于F島的核電站敞贡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏摄职。R本人自食惡果不足惜誊役,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谷市。 院中可真熱鬧蛔垢,春花似錦、人聲如沸迫悠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至甫男,卻和暖如春且改,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背板驳。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工又跛, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人若治。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓慨蓝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親端幼。 傳聞我的和親對象是個殘疾皇子礼烈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容