NSURLProtocol實現(xiàn)WebView緩存圖片

什么是 NSURLProtocol

NSURLProtocol作為URL Loading System中的一個獨立部分存在物邑,能夠攔截所有的URL Loading System發(fā)出的網(wǎng)絡(luò)請求猴凹,攔截之后便可根據(jù)需要做各種自定義處理扔亥,是iOS網(wǎng)絡(luò)層實現(xiàn)AOP(面向切面編程)的終極利器

NSURLProtocol是URL Loading System的重要組成部分帆竹。
首先雖然名叫NSURLProtocol杭煎,但它卻不是協(xié)議哼拔。它是一個抽象類概行。我們要使用它的時候需要創(chuàng)建它的一個子類

NSURLProtocol在iOS系統(tǒng)中大概處于這樣一個位置:

image.png

NSURLProtocol能攔截哪些網(wǎng)絡(luò)請求

NSURLProtocol能攔截所有基于URL Loading System的網(wǎng)絡(luò)請求。

image.png

可以攔截的網(wǎng)絡(luò)請求包括NSURLSession儒鹿,NSURLConnection以及UIWebVIew化撕。基于CFNetwork的網(wǎng)絡(luò)請求挺身,以及WKWebView的請求是無法攔截的『钏現(xiàn)在主流的iOS網(wǎng)絡(luò)庫,例如AFNetworking章钾,Alamofire等網(wǎng)絡(luò)庫都是基于NSURLSession或NSURLConnection的墙贱,所以這些網(wǎng)絡(luò)庫的網(wǎng)絡(luò)請求都可以被NSURLProtocol所攔截。還有一些年代比較久遠(yuǎn)的網(wǎng)絡(luò)庫贱傀,例如ASIHTTPRequest惨撇,MKNetwokit等網(wǎng)路庫都是基于CFNetwork的,所以這些網(wǎng)絡(luò)庫的網(wǎng)絡(luò)請求無法被NSURLProtocol攔截府寒。

NSURLProtocol的使用

自定義NSURLProtocol的子類YHCacheURLProtocol然后在發(fā)起請求前注冊

對于基于NSURLConnection或者使用[NSURLSession sharedSession]創(chuàng)建的網(wǎng)絡(luò)請求魁衙,調(diào)用registerClass方法即可

[NSURLProtocol registerClass:[YHCacheURLProtocol class]];

對于基于NSURLSession的網(wǎng)絡(luò)請求,需要通過配置NSURLSessionConfiguration對象的protocolClasses屬性株搔。

 NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
 sessionConfiguration.protocolClasses = @[[YHCacheURLProtocol class]];

攔截

在攔截到網(wǎng)絡(luò)請求后剖淀,NSURLProtocol會依次執(zhí)行下列方法:

  1. + (BOOL)canInitWithRequest:(NSURLRequest *)request
/*
 (1).處理返回YES,不處理返回NO
 (2).打標(biāo)簽纤房,已經(jīng)處理過的不在處理
 */
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    NSString *url = request.URL.absoluteString;
    
    if ([url containsString:@".jpg"] || [url containsString:@".jpeg"] || [url containsString:@".png"]) {
        
        //處理過的纵隔,放過
        if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
            return NO;
        }
        
        return YES;
    }
    return NO;
}

該方法會拿到request的對象,我們可以通過該方法的返回值來篩選request是否需要被NSURLProtocol做攔截處理炮姨。

這里我們需要緩存圖片的request 所以判斷url是否是圖片格式再進(jìn)行攔截
[NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]
該方法是判斷這個request是否處理過的捌刮。在后面處理request的時候會對request進(jìn)行標(biāo)記

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

在該方法中,我們可以對request進(jìn)行處理舒岸。例如修改頭部信息等绅作。最后返回一個處理后的request實例。如果需要處理 對request進(jìn)行mutableCopy然后進(jìn)行修改

這里我們只對WebView的圖片做緩存 所以對request不做修改

  1. - (void)startLoading
- (void)startLoading
{
    NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
    NSString *url = self.request.URL.absoluteString;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        UIImage *image = [[SDWebImageManager sharedManager].imageCache imageFromCacheForKey:url];
       
        if (image) {
            NSData *data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:SDImageFormatUndefined];
            
            NSURLResponse *res = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:@"image/*;q=0.8" expectedContentLength:data.length textEncodingName:nil];
            
            [self.client URLProtocol:self didReceiveResponse:res cacheStoragePolicy:NSURLCacheStorageNotAllowed];
            [self.client URLProtocol:self didLoadData:data];
            [self.client URLProtocolDidFinishLoading:self];
        } else {
            
            //request處理過的放進(jìn)去
            [NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
            
            [[SDWebImageManager sharedManager].imageDownloader downloadImageWithURL:self.request.URL options:SDWebImageDownloaderHighPriority progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
                
                if (error) {
                    [self.client URLProtocol:self didFailWithError:error];
                } else {
                    [[SDWebImageManager sharedManager].imageCache storeImageDataToDisk:data forKey:url];
                    
                    NSURLResponse *res = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:@"image/*;q=0.8" expectedContentLength:data.length textEncodingName:nil];
                     [self.client URLProtocol:self didReceiveResponse:res cacheStoragePolicy:NSURLCacheStorageNotAllowed];
                    [self.client URLProtocol:self didLoadData:data];
                    [self.client URLProtocolDidFinishLoading:self];
                }
                
            }];
        }
    });
}

在這個方法里 處理我們攔截到的webview圖片加載的request

在這里判斷url對應(yīng)的圖片在本地是否有緩存蛾派,如果有 就直接調(diào)用

[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];

如果不存在緩存俄认,就去請求

/*!
    @abstract Returns the NSURLProtocolClient of the receiver. 
    @result The NSURLProtocolClient of the receiver.  
*/
@property (nullable, readonly, retain) id <NSURLProtocolClient> client;

client是一個遵守了NSURLProtocolClient協(xié)議的對象

NSURLProtocolClient協(xié)議主要用到下面幾個方法:


- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;

- (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市洪乍,隨后出現(xiàn)的幾起案子梭依,更是在濱河造成了極大的恐慌,老刑警劉巖典尾,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件役拴,死亡現(xiàn)場離奇詭異,居然都是意外死亡钾埂,警方通過查閱死者的電腦和手機河闰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褥紫,“玉大人姜性,你說我怎么就攤上這事∷杩迹” “怎么了部念?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我儡炼,道長妓湘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任乌询,我火速辦了婚禮榜贴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妹田。我一直安慰自己唬党,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布鬼佣。 她就那樣靜靜地躺著驶拱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晶衷。 梳的紋絲不亂的頭發(fā)上蓝纲,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音房铭,去河邊找鬼驻龟。 笑死,一個胖子當(dāng)著我的面吹牛缸匪,可吹牛的內(nèi)容都是我干的翁狐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凌蔬,長吁一口氣:“原來是場噩夢啊……” “哼露懒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起砂心,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤懈词,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后辩诞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坎弯,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年译暂,在試婚紗的時候發(fā)現(xiàn)自己被綠了抠忘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡外永,死狀恐怖崎脉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伯顶,我是刑警寧澤囚灼,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布骆膝,位于F島的核電站,受9級特大地震影響灶体,放射性物質(zhì)發(fā)生泄漏阅签。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一赃春、第九天 我趴在偏房一處隱蔽的房頂上張望愉择。 院中可真熱鬧劫乱,春花似錦织中、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至殖妇,卻和暖如春刁笙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谦趣。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工疲吸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人前鹅。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓摘悴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舰绘。 傳聞我的和親對象是個殘疾皇子蹂喻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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

  • 前言:NSURLProtocol是NSURLConnection的handle類, 它更像一套協(xié)議,如果遵守這套協(xié)...
    天下林子閱讀 2,188評論 0 3
  • 概覽 緩存設(shè)計應(yīng)該是每個客戶端程序開發(fā)所必須考慮的問題,如果同一個功能需要多次訪問捂寿,而每次訪問都重新請求的話勢必降...
    字節(jié)跳動技術(shù)團隊閱讀 1,238評論 1 22
  • 由于最近項目要區(qū)分是否為移動端口四,因此需要手動將URL請求的域名重定向到指定的IP地址,但是由于請求可能是通過NSU...
    All_Be_Alright閱讀 385評論 0 0
  • 四月初秦陋,鳥獸在春日風(fēng)情中恣意嚎叫撒潑蔓彩,我們讀書人在渾渾噩噩度日中一邊懶散一邊覺醒。 每天都要從圖書館后路過驳概,今早才...
    如丹閱讀 435評論 2 0
  • “疼愛就是我不講理 赤嚼,你也讓我?guī)追帧?”
    NIU暖秋閱讀 128評論 0 0