公司新項(xiàng)目里面,主要是以原生作為框架,詳情頁面有web頁面負(fù)責(zé)展示笛园。iOS這邊是用WKWebView來加載web頁面,但是由于網(wǎng)絡(luò)環(huán)境和頁面復(fù)雜度的問題笨农,iOS 端和安卓 的web加載速度很慢褂乍。(客戶服務(wù)器域名的問題)
1、把所有web的資源包放到本地:問題依舊在
2桑涎、web頁面共有的資源包放到原生本地彬向,web頁面加載時(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á)到“秒開”webview的效果。
對(duì)于iOS而言禁谦,這就需要用到NSURLProtocol這個(gè)神器了胁黑。接下來,分析下它到底是什么東西州泊,我們?cè)趺蠢盟_(dá)到上述效果丧蘸。
NSURLProtocol:它能夠讓你去重新定義蘋果的URL加載系統(tǒng)(URL Loading System)的行為,URL Loading System里有許多類用于處理URL請(qǐng)求遥皂,比如NSURL力喷,NSURLRequest刽漂,NSURLConnection和NSURLSession等。當(dāng)URL Loading System使用NSURLRequest去獲取資源的時(shí)候弟孟,它會(huì)創(chuàng)建一個(gè)NSURLProtocol子類的實(shí)例贝咙,你不應(yīng)該直接實(shí)例化一個(gè)NSURLProtocol,NSURLProtocol看起來像是一個(gè)協(xié)議拂募,但其實(shí)這是一個(gè) 類庭猩,而且必須使用該類的子類,并且需要被注冊(cè)没讲。
換句話說眯娱,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加一些東西什么的于样,我們這里沒什么要改的,所以直接返回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頁面加載的資源包文件名(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) {
//本地資源包沒有所需的文件,加載網(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];
//硬編碼 開始嵌入本地資源到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)于攔截并加載本地資源包的代碼操作就沒有了威沫,剩下還有一步,就是在加載web頁面之前洼专,對(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é)語:之前單純加載web頁面的時(shí)候屁商,加載時(shí)間基本都是在兩秒以上句柠,現(xiàn)在基本控制在兩秒以內(nèi)了 。