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

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

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

网络请求的拦截

  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.
//

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

@interface NSURLProtocolCustom ()

@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

更多优质文章,可以微信扫码关注:
---------------------
作者:JackLee18
来源:CSDN
原文:https://blog.csdn.net/hanhailong18/article/details/79394856
版权声明:本文为博主原创文章,转载请附上博文链接!

WKWebView实现网页静态资源优先从本地加载的更多相关文章

  1. 关于document.write()加载JS等静态资源 和 异步async加载JS

    现流行浏览器对于静态资源的预加载 传统的浏览器,对于静态资源加载,会阻塞 HTML 解析器的线程进行,无论内联还是外链. 例如: <script src="test1.js" ...

  2. Unity3D基础学习之AssetBundle 资源包创建与加载

    前几天做了AssentBundle的例子,遇到了问题,在论坛上问了三天都没人解答,最后在一个朋友的帮助下解决了.下面介绍AssentBundle. AssetBundles让你通过WWW类流式加载额外 ...

  3. Android 如何本地加载pdf文件

    大部分app打开pdf文件是通过intent调起手机中能打开pdf文件的工具,来查看pdf文件,如果需求是,用户在app内下载好pdf文件后,不通过第三方的工具,本地打开. 这样的需求要怎么实现呢?上 ...

  4. ios -网络加载json和本地加载json

    1网络加载json的时候,要在模型的实现文件里写: - (void)setValue:(id)value forKey:(NSString *)key { } 2本地加载json的时候,要在模型的实现 ...

  5. Unity3d-WWW实现图片资源显示以及保存和本地加载

    本文固定连接:http://blog.csdn.net/u013108312/article/details/52712844 WWW实现图片资源显示以及保存和本地加载 using UnityEngi ...

  6. visual studio 2010 自带reporting报表本地加载的使用

    原文:visual studio 2010 自带reporting报表本地加载的使用 在这家公司时间不长,接触都是之前没玩过的东东,先是工作流引擎和各种邮件短信的审核信息,后又是部署reporting ...

  7. Tensorflow 2 flower_photos花卉数据集手动下载、离线安装、本地加载、快速读取

    Tensorflow 2 flower_photos花卉数据集手动下载.离线安装.本地加载.快速读取 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.c ...

  8. Tensorflow 2 Cifar10离线数据集手动下载、离线安装、本地加载、快速读取

    Tensorflow 2 Cifar10离线数据集手动下载.离线安装.本地加载.快速读取 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com   查 ...

  9. Swift - 网页控件(UIWebView)加载本地数据,文件

    使用UIWebView加载本地数据或资源有如下三种方式: 1,使用loadHTMLString方法加载HTML内容 2,使用loadRequest方法加载本地资源(也可用于加载服务器资源) 3,先将内 ...

随机推荐

  1. JSP显示页面和数据库乱码

    页面 和 数据库编码都是UTF-8,但就是奇怪. 指定Tomcat的编码为UTF-8 就行了

  2. SpringBoot读取配置文件(从classpath/file读取yml/properties文件)

    一.读取properties文件 使用配置项@PropertySource   二.读取yml文件 启动类添加下面代码: @Bean public static PropertySourcesPlac ...

  3. curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.报错

    curl https 网站 出现报错 解决办法: You can use the domain name as usual but override the resolver like so: cur ...

  4. HRMS(人力资源管理系统)-SaaS架构设计-概要设计实践

    一.开篇 前期我们针对架构准备阶段及需求分析这块我们写了2篇内容<HRMS(人力资源管理系统)-从单机应用到SaaS应用-架构分析(功能性.非功能性.关键约束)-上篇><HRMS(人 ...

  5. spring @Transactional 事务注解

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = ...

  6. SSE图像算法优化系列二十五:二值图像的Euclidean distance map(EDM)特征图计算及其优化。

    Euclidean distance map(EDM)这个概念可能听过的人也很少,其主要是用在二值图像中,作为一个很有效的中间处理手段存在.一般的处理都是将灰度图处理成二值图或者一个二值图处理成另外一 ...

  7. Centos如何安装163yum源

    如果Centos使用系统自带的yum源,在用yum进行安装的时候,速度会受到限制,所以我们需要替换为国内的yum源,一般会选择163源,速度比较快包也比较全,使用yum进行安装的时候可以大大节省时间 ...

  8. webstorm+nodejs+express配置

  9. maven error: element dependency can not have character children

    就是Mavn pom.xml的解析错误,因为dependency这个标签中有不可见的垃圾字符,解决方法就是删掉重新打字进去就可以了. references: https://stackoverflow ...

  10. Docker Mongo数据库主主同步配置方法

    一.背景 不多说,请看第一篇<Docker Mongo数据库主从同步配置方法> 二.具体操作方法 1.创建目录,如创建~/test/mongo_sr1和-/test/mongo_sr2两个 ...