前言:最近微信的小游戏跳一跳特别的火,顺便也让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. Accumulator<Long> implements of JavaSparkContext in Spark1.x

    As we all know , up to Spark 1.6.2, JavaSparkContext only provides two kinds of accumulators: Intege ...

  2. Spark2.3(四十):如何使用java通过yarn api调度spark app,并根据appId监控任务,关闭任务,获取任务日志

    背景: 调研过OOZIE和AZKABA,这种都是只是使用spark-submit.sh来提交任务,任务提交上去之后获取不到ApplicationId,更无法跟踪spark application的任务 ...

  3. 【ASP.NET Core】EF Core 模型与数据库的创建

    大家好,欢迎收看由土星卫视直播的大型综艺节目——老周吹逼逼. 今天咱们吹一下 EF Core 有关的话题.先说说模型和数据库是怎么建起来的,说装逼一点,就是我们常说的 “code first”.就是你 ...

  4. JAVA项目中引用Logback的方法

    一.简介 本文主要讲JAVA项目中引入Logback的方法. 二.解决 1.引入依赖. <!--Begin LogBack Log--> <!-- https://mvnreposi ...

  5. ubuntu下安装配置apache2与php

    1:安装apache2 sudo apt install apache2 2:修改端口号 sudo vi /etc/apache2/ports.conf 3:修改跟目录 在 /etc/apache2/ ...

  6. Fiddler抓包_重定向AutoResponder

    1. 文件及图片替换(Enable rules) 目的:允许从本地返回文件,代替服务器响应,而不用将文件发布到服务器[可用正式环境验证本地文件] 步骤一:抓页面http://ir.baidu.com/ ...

  7. Linux netstat常用命令

    1.统计80端口连接数netstat -nat|grep -i "80"|wc -l 2.统计httpd协议连接数(查看Apache的并发请求数及其TCP连接状态)ps -ef|g ...

  8. celery --分布式任务队列

    一.介绍 celery是一个基于python开发的分布式异步消息任务队列,用于处理大量消息,同时为操作提供维护此类系统所需的工具. 它是一个任务队列,专注于实时处理,同时还支持任务调度.如果你的业务场 ...

  9. mongodb配置文件解说(转载)

    启动方式 ./bin/mongod -f mongodb.conf 会看到 about to fork child process, waiting until server is ready for ...

  10. (转)常用的 TCP KeepAlive 参数

    socket编程里通过setsockopt系统调用针对单独的socket进行设置,可以覆盖Linux Kernel的选项.举个例子,以我的系统默认设置为例,kernel默认设置的tcpkeepaliv ...