前言:最近微信的小遊戲跳一跳特別的火,順便也讓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