WKWebView 優(yōu)先加載本地資源包

公司新項(xiàng)目里面,主要是以原生作為框架,詳情頁(yè)面有web頁(yè)面負(fù)責(zé)展示。iOS這邊是用WKWebView來(lái)加載web頁(yè)面,但是由于網(wǎng)絡(luò)環(huán)境和頁(yè)面復(fù)雜度的問(wèn)題,iOS 端和安卓 的web加載速度很慢。(客戶(hù)服務(wù)器域名的問(wèn)題)
1、把所有web的資源包放到本地:?jiǎn)栴}依舊在
2、web頁(yè)面共有的資源包放到原生本地,web頁(yè)面加載時(shí),先判斷本地是否有資源包,優(yōu)先加載本地資源包。
參考:https://segmentfault.com/a/1190000005732602
https://blog.csdn.net/u011154007/article/details/68068172
https://blog.csdn.net/hanhailong18/article/details/79394856
核心原理:
對(duì)H5請(qǐng)求進(jìn)行攔截,如果本地已經(jīng)有對(duì)應(yīng)的靜態(tài)資源文件,則直接加載,這樣就能達(dá)到“秒開(kāi)”webview的效果。

對(duì)于iOS而言,這就需要用到NSURLProtocol這個(gè)神器了。接下來(lái),分析下它到底是什么東西,我們?cè)趺蠢盟_(dá)到上述效果。

NSURLProtocol:它能夠讓你去重新定義蘋(píng)果的URL加載系統(tǒng)(URL Loading System)的行為,URL Loading System里有許多類(lèi)用于處理URL請(qǐng)求,比如NSURL,NSURLRequest,NSURLConnection和NSURLSession等。當(dāng)URL Loading System使用NSURLRequest去獲取資源的時(shí)候,它會(huì)創(chuàng)建一個(gè)NSURLProtocol子類(lèi)的實(shí)例,你不應(yīng)該直接實(shí)例化一個(gè)NSURLProtocol,NSURLProtocol看起來(lái)像是一個(gè)協(xié)議,但其實(shí)這是一個(gè) 類(lèi),而且必須使用該類(lèi)的子類(lèi),并且需要被注冊(cè)。

換句話說(shuō),NSURLProtocol能攔截所有當(dāng)前app下的網(wǎng)絡(luò)請(qǐng)求,并且能自定義地進(jìn)行處理。
直接上代碼

#import "NSURLProtocolCustom.h"
#import <CoreFoundation/CoreFoundation.h>
#import <MobileCoreServices/MobileCoreServices.h>
static NSString* const FilteredKey = @"FilteredKey";

@interface NSURLProtocolCustom()<NSURLConnectionDataDelegate>
@property(nonatomic, strong)NSURLConnection *coonection;
@end


@implementation NSURLProtocolCustom
//這個(gè)方法的作用是判斷當(dāng)前protocol是否要對(duì)這個(gè)request進(jìn)行處理(所有的網(wǎng)絡(luò)請(qǐng)求都會(huì)走到這里,所以我們只需要對(duì)我們產(chǎn)生的request進(jìn)行處理即可)。
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    NSString *extension = request.URL.pathExtension;
    BOOL isSource = [@[@"png", @"jpeg", @"gif", @"jpg", @"js", @"css"] indexOfObjectPassingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        return [extension compare:obj options:NSCaseInsensitiveSearch] == NSOrderedSame;
    }] != NSNotFound;
    return [NSURLProtocol propertyForKey:FilteredKey inRequest:request] == nil && isSource;
}
//可以對(duì)request進(jìn)行預(yù)處理,比如對(duì)header加一些東西什么的,我們這里沒(méi)什么要改的,所以直接返回request就好了。
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}
//我們這里需要做一件事,就是自己拼裝httpResponse,并且返回給url load system,然后到了webview那一層,會(huì)收到response,對(duì)于webview而言,加載本地和走網(wǎng)絡(luò)拿到的response是完全一樣的。所以上述代碼展示了如何拼裝一個(gè)httpResponse,當(dāng)組裝完成后,需要調(diào)用self.client將數(shù)據(jù)傳出去。
- (void)startLoading
{
    //fileName 獲取web頁(yè)面加載的資源包文件名(js  css等)
    NSString *fileName = [super.request.URL.absoluteString componentsSeparatedByString:@"/"].lastObject;

    //這里是獲取本地資源路徑 如:png,js等
    NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
    NSLog(@"fileName is %@ path=%@",fileName,path);
    if (!path) {
       //本地資源包沒(méi)有所需的文件,加載網(wǎng)絡(luò)請(qǐng)求
        NSMutableURLRequest *newrequest = [self.request mutableCopy];
        newrequest.allHTTPHeaderFields = self.request.allHTTPHeaderFields;
        [NSURLProtocol setProperty:@YES forKey:FilteredKey inRequest:newrequest];
        self.coonection = [NSURLConnection connectionWithRequest:newrequest delegate:self];
        return;
    }
    //根據(jù)路徑獲取MIMEType
    CFStringRef pathExtension = (__bridge_retained CFStringRef)[path pathExtension];
    CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, NULL);
    CFRelease(pathExtension);
    
    //The UTI can be converted to a mime type:
    NSString *mimeType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType);
    if (type != NULL)
        CFRelease(type);
    
    //加載本地資源
    NSData *data = [NSData dataWithContentsOfFile:path];
    [self sendResponseWithData:data mimeType:mimeType];
}

- (void)stopLoading
{
    [self.coonection cancel];
    NSLog(@"stopLoading, something went wrong!");
}

- (void)sendResponseWithData:(NSData *)data mimeType:(nullable NSString *)mimeType
{
    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 *response = [[NSHTTPURLResponse alloc]initWithURL:self.request.URL statusCode:200 HTTPVersion:@"1.1" headerFields:header];
    // 這里需要用到MIMEType
    //NSURLResponse *response = [[NSURLResponse alloc] initWithURL:super.request.URL MIMEType:mimeType expectedContentLength:-1 textEncodingName:nil];
    
    //硬編碼 開(kāi)始嵌入本地資源到web中
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [[self client] URLProtocol:self didLoadData:data];
    [[self client] URLProtocolDidFinishLoading:self];
}
#pragma 代理
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    [self.client URLProtocol:self didLoadData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
    [self.client URLProtocolDidFinishLoading:self];
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    [self.client URLProtocol:self didFailWithError:error];
}

@end

目前,關(guān)于攔截并加載本地資源包的代碼操作就沒(méi)有了,剩下還有一步,就是在加載web頁(yè)面之前,對(duì)自定義的NSURLProtocol 進(jìn)行注冊(cè),使上面的代碼實(shí)現(xiàn)對(duì)資源的攔截

-(void)setNSURLProtocolCustom{
    //注冊(cè)
    [NSURLProtocol registerClass:[NSURLProtocolCustom class]];
    //實(shí)現(xiàn)攔截功能,這個(gè)是核心
    Class cls = NSClassFromString(@"WKBrowsingContextController");
    SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
    if ([(id)cls respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [(id)cls performSelector:sel withObject:@"http"];
        [(id)cls performSelector:sel withObject:@"https"];
#pragma clang diagnostic pop
    }
}

結(jié)語(yǔ):之前單純加載web頁(yè)面的時(shí)候,加載時(shí)間基本都是在兩秒以上,現(xiàn)在基本控制在兩秒以?xún)?nèi)了 。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,070評(píng)論 25 709
  • 今天起了個(gè)大早,趕了個(gè)晚集。等了半小時(shí),都不見(jiàn)206的蹤影。索性走路到幼兒園,趕9:20的趟…蟲(chóng)子說(shuō),背著書(shū)包,他...
    溦歷閱讀 204評(píng)論 0 0
  • 【0615我在悅讀】凡楚 2018年第93次打卡, 書(shū)名:書(shū)都不會(huì)讀,你還想成功 作者:二志成、鄭會(huì)一 篇目:p1...
    凡楚_929d閱讀 189評(píng)論 0 0
  • 怎樣達(dá)到雙贏? 做到兩點(diǎn)一是做好自己,使自己成為獨(dú)立、有安全感、有原則的人。二是避免被競(jìng)爭(zhēng)想法和攀比思想左右。 另...
    徐慧棣閱讀 204評(píng)論 0 0

友情鏈接更多精彩內(nèi)容