WKWebView实现网页静态资源优先从本地加载

前言:最近微信的小游戏跳一跳特别的火,顺便也让h5小游戏更加的火热。另外微信小程序,以及支付宝的小程序都是用H5写的。不管是小游戏仍是小程序,这些都须要加载更多的资源文件,处理更多的业务。这些都对网页加载的速度提出了较高的要求。UIWebView因为占用内存大,释放不掉一直备受诟病。并且目前是大多数的app支持的最低版本都是从iOS 8开始的。我这里主要针对WKWebView来讲一下。css

资源包压缩下载VS静态资源文件下载web

  根据不一样的业务需求,不一样的app对于资源文件的处理情形是不一样的。以12306app为例。选择了下载资源压缩到沙盒的策略,列车班次发生调整时,调用接口,强制下载资源压缩包到本地。注释:可是WKWebView加载本地资源文件,有些麻烦,后续会是专门深刻研究下。因为强制下载资源包的形式用户体验不是特别好,不少小游戏,以及小程序为了更好的用户体验一般选择隐性下载静态资源文件的形式,加载时优先使用本地已下载的资源文件进行加载,不只能够提升加载速度,并且还能够为用户节省流量。json

网络请求的拦截小程序

  NSURLProtocol相信不少小伙伴都挺据说并使用过。记得很早一段时间,你们对于WKWebView使用NSURLProtocol进行网络请求进行拦截没有很好的办法,还好不知道哪位大神最终找到了解决的办法,在此万分感谢。代码入以下:微信小程序

//2.注册
[NSURLProtocol registerClass:[NSURLProtocolCustom class]];
//3.实现拦截功能,这个是核心
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
}
1
2
3
4
5
6
7
8
9
10
11
12
加载时优先加载本地资源文件微信

  对WKWebView发出的网络请求进行拦截后,咱们须要对资源文件的进行判断本,判断本地是否有对应的资源文件,若是有的话优先加载本地的资源文件。对于资源文件的匹配,我这里将网络请求中资源文件的url进行MD5序列化后,做为资源文件的名字。代码以下:网络

//
// NSURLProtocolCustom.m
// WKWebViewDemo1
//
// Created by JackLee on 2018/2/27.
// Copyright © 2018年 JackLee. All rights reserved.
//app

#import "NSURLProtocolCustom.h"
#import "NSString+MD5.h"
#import <JKSandBoxManager/JKSandBoxManager.h>
#import <AFNetworking/AFNetworking.h>编码

@interface NSURLProtocolCustom ()atom

@property (nonatomic, strong) AFURLSessionManager *manager;

@end

static NSString* const FilteredKey = @"FilteredKey";

@implementation NSURLProtocolCustom
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSString *extension = request.URL.pathExtension;
BOOL isSource = [[self resourceTypes] 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;
}

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

- (void)startLoading
{
NSMutableURLRequest *mutableReqeust = [super.request mutableCopy];
//标记该请求已经处理
[NSURLProtocol setProperty:@YES forKey:FilteredKey inRequest:mutableReqeust];
NSString *fileName = [NSString stringWithFormat:@"%@.%@",[super.request.URL.absoluteString MD5Hash],super.request.URL.pathExtension];

NSString *gameId = [[NSUserDefaults standardUserDefaults] stringForKey:@"GameId"];
NSString *nameSpace = [NSString stringWithFormat:@"GameId%@",gameId];
NSString *fileDir =[JKSandBoxManager createCacheFilePathWithFolderName:nameSpace];
NSString *filePath =[fileDir stringByAppendingString:[NSString stringWithFormat:@"/%@",fileName]];
NSLog(@"targetpath %@",filePath);

if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { //文件不存在,去下载
[self downloadResourcesWithRequest:[mutableReqeust copy]];
return;
}
//加载本地资源
NSData *data = [NSData dataWithContentsOfFile:filePath];
[self sendResponseWithData:data mimeType:[self getMimeTypeWithFilePath:filePath]];
}

- (void)stopLoading
{

}

- (void)sendResponseWithData:(NSData *)data mimeType:(nullable NSString *)mimeType
{
// 这里须要用到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];
}

/**
* manager的懒加载
*/
- (AFURLSessionManager *)manager {
if (!_manager) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// 1. 建立会话管理者
_manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
}
return _manager;
}

////下载资源文件
- (void)downloadResourcesWithRequest:(NSURLRequest *)request{

NSString *fileName = [NSString stringWithFormat:@"%@.%@",[super.request.URL.absoluteString MD5Hash],super.request.URL.pathExtension];

NSString *gameId = [[NSUserDefaults standardUserDefaults] stringForKey:@"GameId"];
NSString *nameSpace = [NSString stringWithFormat:@"GameId%@",gameId];
NSString *fileDir =[JKSandBoxManager createCacheFilePathWithFolderName:nameSpace];
NSString *targetFilePath =[fileDir stringByAppendingString:[NSString stringWithFormat:@"/%@",fileName]];

NSURLSessionDownloadTask *downloadTask = [self.manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress) {
// 下载进度

} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *path = [NSURL fileURLWithPath:JKSandBoxPathTemp];
return [path URLByAppendingPathComponent:[NSString stringWithFormat:@"%@",fileName]];

} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
[JKSandBoxManager moveFileFrom:filePath.path to:targetFilePath];
NSLog(@"targetpath %@",targetFilePath);
NSData *data = [NSData dataWithContentsOfFile:targetFilePath];
[self sendResponseWithData:data mimeType:[self getMimeTypeWithFilePath:targetFilePath]];
}];

// 4. 开启下载任务
[downloadTask resume];

}

- (NSString *)getMimeTypeWithFilePath:(NSString *)filePath{
CFStringRef pathExtension = (__bridge_retained CFStringRef)[filePath 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);

return mimeType;
}

+ (NSArray *)resourceTypes{
return @[@"png", @"jpeg", @"gif", @"jpg",@"jpg",@"json", @"js", @"css",@"mp3",@"fnt"];
}


@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
其中,这里对资源文件的下载没有使用NSURLConnection,主要是NSURLConnection在iOS 9 之后就被废弃掉了。我这里用了AFnetworking进行处理。

处理资源文件失效

  对着小程序或者小游戏的更新。某些资源文件会失效,若是不及时清除的话,就会很是的占用资源。针对这种状况,咱们可让用户主动删除相关的资源文件,也能够给资源文件设置有效期,进行自动的删除操做。 demo以下:demo

相关文章
相关标签/搜索