WKWebView实现网页静态资源优先从本地加载
前言:最近微信的小游戏跳一跳特别的火,顺便也让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实现网页静态资源优先从本地加载的更多相关文章
- 关于document.write()加载JS等静态资源 和 异步async加载JS
现流行浏览器对于静态资源的预加载 传统的浏览器,对于静态资源加载,会阻塞 HTML 解析器的线程进行,无论内联还是外链. 例如: <script src="test1.js" ...
- Unity3D基础学习之AssetBundle 资源包创建与加载
前几天做了AssentBundle的例子,遇到了问题,在论坛上问了三天都没人解答,最后在一个朋友的帮助下解决了.下面介绍AssentBundle. AssetBundles让你通过WWW类流式加载额外 ...
- Android 如何本地加载pdf文件
大部分app打开pdf文件是通过intent调起手机中能打开pdf文件的工具,来查看pdf文件,如果需求是,用户在app内下载好pdf文件后,不通过第三方的工具,本地打开. 这样的需求要怎么实现呢?上 ...
- ios -网络加载json和本地加载json
1网络加载json的时候,要在模型的实现文件里写: - (void)setValue:(id)value forKey:(NSString *)key { } 2本地加载json的时候,要在模型的实现 ...
- Unity3d-WWW实现图片资源显示以及保存和本地加载
本文固定连接:http://blog.csdn.net/u013108312/article/details/52712844 WWW实现图片资源显示以及保存和本地加载 using UnityEngi ...
- visual studio 2010 自带reporting报表本地加载的使用
原文:visual studio 2010 自带reporting报表本地加载的使用 在这家公司时间不长,接触都是之前没玩过的东东,先是工作流引擎和各种邮件短信的审核信息,后又是部署reporting ...
- Tensorflow 2 flower_photos花卉数据集手动下载、离线安装、本地加载、快速读取
Tensorflow 2 flower_photos花卉数据集手动下载.离线安装.本地加载.快速读取 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.c ...
- Tensorflow 2 Cifar10离线数据集手动下载、离线安装、本地加载、快速读取
Tensorflow 2 Cifar10离线数据集手动下载.离线安装.本地加载.快速读取 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 查 ...
- Swift - 网页控件(UIWebView)加载本地数据,文件
使用UIWebView加载本地数据或资源有如下三种方式: 1,使用loadHTMLString方法加载HTML内容 2,使用loadRequest方法加载本地资源(也可用于加载服务器资源) 3,先将内 ...
随机推荐
- CSS_盒子模型
2016-10-22 <css入门经典>第6章 1.每个HTML元素对应于一个显示盒子,但不是所有的元素都显示在屏幕上. 2.HTML元素显示为CSS显示盒子的真正方法称为“可视格式化方式 ...
- sench touch 页面跳转
下面是我做的一个简单的登录页面登录成功后跳转页面 首页要在app.js 里面添加 1.视图层 登录页面 Ext.define('MyApp.view.Login', { extend: 'Ext. ...
- Spring源码分析 之浅谈设计模式
一直想专门写个Spring源码的博客,工作了,可以全身性的投入到互联网行业中.虽然加班很严重,但是依然很开心.趁着凌晨有时间,总结总结. 首先spring,相信大家都很熟悉了. 1.轻量级 零配置, ...
- 免费 web 后台管理 网站
使用java自主开发的后台管理软件, 访问地址:http://wx.yanquanguoke.top 登录账号:test 密码:test 欢迎大家使用,有什么想法的可以交流. 进群搜索微信号:a1 ...
- Unity中的定时器与延时器
JavaScript中的定时器与延时器,分别是 setInterval.setTimeout,对应的清理函数是:clearInterval.clearTimeout. 而在Unity中,则分别是:In ...
- xcode10 改动
xcode10 开发环境 比 之前有了稍微的变动 1. 代码块 界面控件 图片资源等 的查看位置发生了变化 之前的开发环境 代码块 统一放在 右侧栏的下方的几个选项中 现在 统一放到了上方 ...
- html input 文本框 只能输入数字,包含输小数点.
<input type="text" id="source_tds" name="source_tds" value="&l ...
- Android Studio打包过程和应用安装过程
三个部分,检查项目和读取基本配置,Gradle Build,Apk Install和LaunchActivity. 应用安装到手机,会复制APK安装包到data/app目录下,解压并扫描安装包,把de ...
- redmine在linux上的mysql性能优化方法与问题排查方案
iredmine的linux服务器mysql性能优化方法与问题排查方案 问题定位: 客户端工具: 1. 浏览器inspect-tool的network timing工具分析 2. 浏览 ...
- Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)
MVVM回顾 经过上一篇文章的介绍,相信你对MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在. View只关心怎样渲染,而ViewModel只 ...