国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

iOS 客戶端基于 WebP 圖片格式的流量?jī)?yōu)化(下)

JiaXinYi / 2324人閱讀

摘要:在客戶端基于圖片格式的流量?jī)?yōu)化上這篇文章中,已經(jīng)介紹了格式圖片的下載使用,僅僅只有這樣還遠(yuǎn)遠(yuǎn)不夠,還需要對(duì)已經(jīng)下載的圖片數(shù)據(jù)進(jìn)行緩存。二圖片緩存關(guān)于的緩存,系統(tǒng)提供了一個(gè)類,。而且,既然是全局影響,肯定要用包起來,防止誤傷其他緩存。

在iOS 客戶端基于 WebP 圖片格式的流量?jī)?yōu)化(上)這篇文章中,已經(jīng)介紹了WebP格式圖片的下載使用,僅僅只有這樣還遠(yuǎn)遠(yuǎn)不夠,還需要對(duì)已經(jīng)下載的圖片數(shù)據(jù)進(jìn)行緩存。

曾經(jīng)有句名言『計(jì)算機(jī)世界有兩大難題,第一是起名字,第二是寫一個(gè)緩存』,鄙人不能同意更多。

在iOS上,重寫一份圖片緩存是不現(xiàn)實(shí)的,而直接修改SDWebImage框架也是不太好的。所以,在SDWebImage的基礎(chǔ)上添加一個(gè)中間層CacheManager比較好。

我感覺,緩存的難度在于,如何準(zhǔn)確命中。的確在開發(fā)的時(shí)候,一大半時(shí)間都是在測(cè)試緩存命中情況,測(cè)試本身就挺麻煩,需要在模擬器的沙盒里面看文件,同時(shí)斷網(wǎng)測(cè)試,需要一些調(diào)試技巧,很多技巧并么有辦法詳盡表述出來,需要所謂的悟性去理解。

一、SDWebImage緩存處理

這一部分,由于SD下載圖片的方法中,url被替換,所以要看懂SD本身的代碼,是什么時(shí)候給緩存一個(gè)確定的key。發(fā)現(xiàn)在

- (id )downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {

    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }
    
    url = [url qd_replaceToWebPURLWithScreenWidth];
    ......

方法中,確定了緩存的key值

    NSString *key = [self cacheKeyForURL:url];

    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {

這也就是之前,為什么要在這個(gè)方法的最前面把URL替換掉,這樣,SD的key值已經(jīng)是保護(hù)WebP格式的圖片URL,這一部分的緩存都可以正常使用,不需要修改。

所以,難度還是在WebView的圖片緩存中,因?yàn)橹半m然是用SD托管WebView中WebP圖片的下載,然而WebView讀緩存卻不能自動(dòng)從SDImageCache中讀取。這樣,需要用NSURLCache來接管WebView的圖片緩存。

二、WebView圖片緩存

關(guān)于WebView的緩存,系統(tǒng)提供了一個(gè)類,NSURLCache。這個(gè)類可以在所有的網(wǎng)絡(luò)請(qǐng)求前查看緩存,并且決定是否緩存(注意:是所有請(qǐng)求)。具體的NSURLCache用法,動(dòng)動(dòng)勤勞的小手Google一下,很多文章可以參考。

我們自己的實(shí)現(xiàn),直接上代碼

@implementation QDURLCache

/**
 *  請(qǐng)求完成決定是否要將response進(jìn)行存儲(chǔ)
 */
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
    NSString* ua = [request valueForHTTPHeaderField:@"User-Agent"];
    if (!EmptyString(ua) && [ua lf_containsSubString:@"AppleWebKit"]) {
        //判斷本次請(qǐng)求是不是請(qǐng)求圖片
        if ([[QDCacheManager defaultManager] isImageRequest:request]) {
            [[QDCacheManager defaultManager] storeImageResponse:cachedResponse forRequest:request];
            return;
        }
        
        //其他請(qǐng)求
        if ([[QDCacheManager defaultManager] shouldCacheExceptImageResponseForRequest:request]) {
            if (![[QDCacheManager defaultManager] storeCachedResponse:cachedResponse forRequest:request]) {
                [super storeCachedResponse:cachedResponse forRequest:request];
                return;
            } else {
                return;
            }
        }
    }
    
    [super storeCachedResponse:cachedResponse forRequest:request];
}

/**
 *  每次發(fā)請(qǐng)求之前會(huì)調(diào)此方法,查看本地是否有緩存
 */
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    NSString* ua = [request valueForHTTPHeaderField:@"User-Agent"];
    if (!EmptyString(ua) && [ua lf_containsSubString:@"AppleWebKit"]) {
        
        if ([[QDCacheManager defaultManager] isImageRequest:request]) { //圖片
            //從本地取圖片
            NSCachedURLResponse *imageCacheResponse = [[QDCacheManager defaultManager] retrieveImageCacheResponseForRequest:request];
            if (imageCacheResponse) {
                return imageCacheResponse;
            } else {
                return [super cachedResponseForRequest:request];
            }
        }
        
        if ([[QDCacheManager defaultManager] shouldCacheExceptImageResponseForRequest:request]) { //其它緩存的東西
            //判斷本地自定義緩存目錄是否存在
            if (![[QDCacheManager defaultManager] cacheAvaliableForRequest:request]) {
                NSCachedURLResponse *response = [super cachedResponseForRequest:request];
                //判斷本地系統(tǒng)緩存目錄是否存在
                if (response.data) {
                    BOOL contentLengthValid = [((NSHTTPURLResponse *)response.response) expectedContentLength] == [response.data length];
                    //判斷是否是有效的文件
                    if (!contentLengthValid) {
                        return response;
                    }
                    
                    //將系統(tǒng)緩存放到自定義的緩存目錄中
                    [[QDCacheManager defaultManager] storeCachedResponse:response forRequest:request];
                } else {
                }
                return response;
            }
            
            //從本地緩存中取出對(duì)應(yīng)的緩存
            NSCachedURLResponse *cachedResponse = [[QDCacheManager defaultManager] retrieveCachedResponseForRequest:request];
            if (cachedResponse) {
                return cachedResponse;
            }
        }
        
    }
    
    return [super cachedResponseForRequest:request];
}

- (void)removeCachedResponseForRequest:(NSURLRequest *)request {
    if ([[QDCacheManager defaultManager] cacheAvaliableForRequest:request]) {
        if (![[QDCacheManager defaultManager] removeCachedResponseForRequest:request]) {
            LogI(@"Failed to remove local cache for request: %@", request.URL);
        }
    } else {
        [super removeCachedResponseForRequest:request];
    }
}

@end

這段代碼并沒有多么難以理解的地方,可以看出來,我們是新建了一個(gè)中間層QDCacheManager,來管理WebView的所有緩存。

而且,既然是全局影響,肯定要用UA包起來,防止誤傷其他緩存。

這一段代碼在調(diào)試的時(shí)候有個(gè)技巧,就是所有super方法的調(diào)用,在測(cè)試階段,全部直接return,防止WebView自身的緩存干擾調(diào)試結(jié)果。這個(gè)方法在很多緩存處理的地方都需要注意,別的地方但凡出現(xiàn)了調(diào)用super方法的,調(diào)試中也一律是直接return的。

既然已經(jīng)用QDCacheManager托管了緩存,URLCache類的任務(wù)就已經(jīng)完成,儲(chǔ)存Response由

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request

而下面:

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request

在NSURLProtocol的startLoading方法執(zhí)行之前,就調(diào)用了。很好理解,因?yàn)檫@個(gè)方法就是取緩存的方法,自然是先取,沒有再去Loading。

這里的邏輯,必須通過大量調(diào)試,反復(fù)驗(yàn)證,不能簡(jiǎn)單套用別人的結(jié)論,甚至官方文檔也要懷疑的態(tài)度來看。因?yàn)椋芏嗟谌娇蚣埽瑫?huì)影響NSURLCache類,我在調(diào)試時(shí),就發(fā)現(xiàn),JSPatch,React Native還有我們的一個(gè)放劫持服務(wù),都有可能影響這個(gè)類中方法的調(diào)用。

下面就轉(zhuǎn)入我們自己的緩存管理方法中去,由于現(xiàn)在關(guān)注的是WebP圖片問題,所以,其他緩存處理就不再展開。

三、中間層CacheManager處理

關(guān)于這個(gè)中間層,主要處理的實(shí)際就是緩存key的問題,因?yàn)檎?qǐng)求的時(shí)候,request里的URL仍然是沒有替換WebP的,所以,需要先用之前qd_defultWebPURLCacheKey方法來獲取真實(shí)圖片緩存key值。
思路的關(guān)鍵就是換key,再取cache,代碼本身就只能靠功底了。

直接上代碼,沒什么好解釋的。

- (BOOL)isImageRequest:(NSURLRequest *)request {
    if (![request.URL.absoluteString qd_isQdailyHost]) {
        return NO;
    }
    NSArray *extensions = @[@".jpg", @".jpeg", @".png", @".gif"];
    for (NSString *extension in extensions) {
        if ([request.URL.absoluteString.lowercaseString lf_containsSubString:extension]){
            return YES;
        }
    }
    return NO;
}

- (void)storeImageResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
    
    NSString *key = [request.URL qd_defultWebPURLCacheKey];
    if ([_imageCache imageFromDiskCacheForKey:key]) {
        return;
    }
    dispatch_async([_imageCache currentIOQueue], ^{
        // 硬盤緩存直接存data,webp格式;內(nèi)存緩存為UIImage,可以直接使用
        [_imageCache storeImageDataToDisk:cachedResponse.data forKey:key];
    });
}

- (NSCachedURLResponse *)retrieveImageCacheResponseForRequest:(NSURLRequest *)request {
    
    NSString *key = [request.URL qd_defultWebPURLCacheKey];
    NSString *defaultPath = [_imageCache defaultCachePathForKey:key];
    
    NSData *data = nil;
    if ([_imageCache imageFromMemoryCacheForKey:key]) {
        UIImage * image = [_imageCache imageFromMemoryCacheForKey:key];
        if ([key lf_containsSubString:@".png"]) {
            data = UIImagePNGRepresentation(image);
        } else {
            data = UIImageJPEGRepresentation(image, 1.0);
        }
    }
    
    if (data &&  data.length != 0) {
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
                                                            MIMEType:[request.URL.absoluteString qd_MIMEType]
                                               expectedContentLength:data.length
                                                    textEncodingName:nil];
        
        return [[NSCachedURLResponse alloc] initWithResponse:response data:data];
    }
    
    data = [NSData dataWithContentsOfFile:defaultPath];
    if (data == nil) {
        data = [NSData dataWithContentsOfFile:[defaultPath stringByDeletingPathExtension]];
    }
    if (data == nil || data.length == 0) {
        [_imageCache removeImageForKey:key fromDisk:YES];
        return nil;
    }

    NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
                                                        MIMEType:[request.URL.absoluteString qd_MIMEType]
                                           expectedContentLength:data.length
                                                textEncodingName:nil];
    
    return [[NSCachedURLResponse alloc] initWithResponse:response data:data];
}

其中currentIOQueue方法,是修改了一下SDImageCache,暴露這個(gè)IOQueue,原來的框架是沒有這個(gè)方法的。

至于為什么圖片硬盤緩存直接用data,因?yàn)檫@里考慮是性能問題,取緩存的時(shí)候,返回的NSURLResponse所攜帶的,肯定還是NSData,如果當(dāng)時(shí)存了UIImage格式,內(nèi)部一樣是轉(zhuǎn)碼成了NSData,而取的時(shí)候,還是按UIImage格式取,再轉(zhuǎn)成NSData返回,相當(dāng)于多了兩次轉(zhuǎn)碼。

內(nèi)存緩存卻沒有這個(gè)問題,因?yàn)镾D的內(nèi)存緩存,用的NSCache,存的就是UIImage對(duì)象,可以直接取出來用。

這里其實(shí)仍然并沒有什么好講的,還是基本的邏輯問題,需要比較嚴(yán)謹(jǐn)?shù)靥幚怼?/p> 四、其他情況的特別處理

我們的app是實(shí)現(xiàn)了wifi預(yù)加載了,然而這一部分也需要與上面完成的緩存體系通用,不然,wifi預(yù)加載的意義就不大。

首先,我們的wifi預(yù)加載,是自己寫了一個(gè)URLSession,所以在下載前替換URL就可以

 for (NSString *urlString in resourcesArray) {
                    if ([urlString isKindOfClass:[NSString class]]) {
                        
                        NSURL *theURL = [NSURL URLWithString:urlString];
                        if ([[QDCacheManager defaultManager] isImageRequest:[NSURLRequest requestWithURL:theURL]]) {
                            theURL = [theURL qd_replaceToWebPURLWithScreenWidth];
                        }
                        if(![[QDCacheManager defaultManager] cacheAvaliableForURL:theURL] && ![[SDImageCache sharedImageCache] diskImageExistsWithKey:theURL.absoluteString]) {
                            __weak QDPrefetcher* weakSelf = self;
                            [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
                            
                      ......

部分代碼如上,關(guān)鍵也在于替換URL時(shí)機(jī)和判斷緩存情況。而下載之后的文件存到哪,是需要處理的。

#pragma mark NSURLSession Delegate

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    NSError *error = nil;
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSString *destinationPath = nil;
    if ([[QDCacheManager defaultManager] isImageRequest:downloadTask.originalRequest]) {
        NSString *key = downloadTask.originalRequest.URL.absoluteString;
        destinationPath = [[SDImageCache sharedImageCache] defaultCachePathForKey:key];
    } else {
        destinationPath = [[QDCacheManager defaultManager] localCachedWebContentPathWithRequest:downloadTask.originalRequest];
    }
    
    if ([fileManager fileExistsAtPath:destinationPath]) {
        [fileManager removeItemAtPath:destinationPath error:nil];
    }
    
    [fileManager copyItemAtPath:location.path toPath:destinationPath error:&error];
    
}

我是在finish的方法里面,把圖片下載的目錄直接copy給SDImageCache的緩存目錄。這樣,SD的緩存里面就有了這些WebP格式的NSData,與之前的代碼邏輯統(tǒng)一,格式統(tǒng)一。

總結(jié)

首先有了一個(gè)心得,看上去很復(fù)雜的功能,可能實(shí)際代碼并不需要自己寫多少,學(xué)會(huì)在前人的基礎(chǔ)上再加工,比如我們現(xiàn)在這套WebP適配,底層仍然是SDWebImage的基本邏輯,我們只不過在上層,加一些判斷和處理,來適應(yīng)業(yè)務(wù)層豐富的功能。

而且,代碼是一步步寫出來的,提前設(shè)想的方案,并不一定能實(shí)現(xiàn),先實(shí)現(xiàn)功能,再優(yōu)化架構(gòu),才是正確的方向。當(dāng)時(shí)在WebURLProtocol里面,繞了很大的彎子,甚至還涉及到了多線程問題,不小心發(fā)現(xiàn)了iOS8,9,10三個(gè)版本的內(nèi)部實(shí)現(xiàn)都在變化,繞開了一個(gè)個(gè)坑,才逐步清晰了整個(gè)邏輯。

總結(jié)整個(gè)方案的邏輯,其實(shí)比較清晰:

首先確定是不是需要被替換的圖片URL,然后所有的替換都采用統(tǒng)一方法,與之配套的key,也用這套方法處理得到他被替換后的URL,保證命中。

然后,無論Native請(qǐng)求還是WebView請(qǐng)求,都用SD托管,避免兩套處理邏輯造成的種種不確定性;

而WebView的緩存,通過一個(gè)中間層處理,再交給SDImageCache,使之與Native請(qǐng)求的數(shù)據(jù)統(tǒng)一,讓兩種圖片請(qǐng)求公用一套緩存,進(jìn)一步重用。

思路大致如此,其他的問題,就需要靠代碼能力了。


一口氣寫完,有不完善的地方,可能日后會(huì)有部分修改。肯定有很多大神的代碼寫得更好,或者寫了更好的方案,也希望多多交流,共同進(jìn)步。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/61789.html

相關(guān)文章

  • iOS 戶端基于 WebP 圖片格式流量優(yōu)化(上)

    摘要:一了解,是一種同時(shí)提供了有損壓縮與無損壓縮的圖片文件格式,是新推出的影像技術(shù),它可讓網(wǎng)頁圖檔有效進(jìn)行壓縮,同時(shí)又不影響圖片格式兼容與實(shí)際清晰度,進(jìn)而讓整體網(wǎng)頁下載速度加快。這里建議所有基于的流量?jī)?yōu)化都最好用的判斷包住,避免帶來問題。 首先,這是一個(gè)基于具體業(yè)務(wù)的組件優(yōu)化方案,我盡量把業(yè)務(wù)邏輯從代碼中抽離出來,部分地方代碼可能有刪減。 現(xiàn)在這個(gè)方案是用于一個(gè)多圖片的新聞?lì)悜?yīng)用,粗略估計(jì)過...

    harryhappy 評(píng)論0 收藏0
  • <轉(zhuǎn)載>圖片流量節(jié)省60%:基于CDNsharpP自適應(yīng)圖片技術(shù)實(shí)踐

    摘要:開啟驗(yàn)證上傳一張新圖片,使用手安卓版本訪問已支持域名的圖片,如果請(qǐng)求帶了,檢查返回圖片格式是否為如果舊的圖片未按預(yù)期返回,返回了或原圖可能是結(jié)點(diǎn)緩存,正常天后過期回源則會(huì)返回圖片。 對(duì)于圖片較多的網(wǎng)站,本文結(jié)合具體案例給出了如何基于CDN的sharpP自適應(yīng)圖片無痛接入方案,據(jù)統(tǒng)計(jì)效果可在原圖基礎(chǔ)上節(jié)省60%-75%的流量。作者:陳忱 出處:騰云閣文章 目前移動(dòng)端運(yùn)營素材大部分依賴圖...

    JerryZou 評(píng)論0 收藏0
  • 【譯】 WebP 支持:超出你想象

    摘要:的支持程度實(shí)際上比你想的可能要好得多。的安卓瀏覽器從版本起開始官方支持最初發(fā)布于年月,版本起開始部分支持。安卓版從起開始支持。而且目前并無添加支持的任何打算。瀏覽器市場(chǎng)份額截至年月的數(shù)據(jù)顯示,占有約的市場(chǎng)份額,以約位居第二。 本文轉(zhuǎn)載自:眾成翻譯譯者:文藺鏈接:http://www.zcfy.cc/article/862原文:https://optimus.keycdn.com/sup...

    Steven 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<