当前位置:首页 » 文件管理 » ios开发webview缓存

ios开发webview缓存

发布时间: 2022-11-20 17:43:49

1. WKWebView网页缓存刷新问题

在开发过程中遇到前端改变图片文字,客户端没有实时刷新出来,抓包发现也没有请求网页相关接口。由于不懂后端的知识,折腾了很久,网上也查找了很多都说需要清除缓存。

这是在网上查找的iOS9以上清除缓存方法

不建议使用上述方法,会浪费用户流量,除非用户手动清除缓存。其实主要原因是后端网页设置的问题,通过head请求获取接口返回信息如下:

上面标粗的是关键,通过测试发现WKWebView是否通过缓存取数据还是重新请求接口取决于 Expires,如上就是缓存时效性是30分钟,想要实时刷新,可以让后端不返回这个字段或者这个过期事件设置短一些,例如1分钟。建议静态网页可以设置长时间,需要实时刷新的建议后端不要设置这个字段,以免客户端无法实时显示。

2. ios 应用删掉了为什么webview之前的缓存还存在

浏览器缓存机制是指通过HTTP协议头里的Cache-Control(或Expires)和Last-Modified(或Etag)等字段来控制文件缓存的机制。这应该是WEB中最早的缓存机制了,是在HTTP协议中实现的,有点不同于DomStorage、AppCache等缓存机制,但本质上是一样的。可以理解为,一个是协议层实现的,一个是应用层实现的。

Cache-Control用于控制文件在本地缓存有效时长。最常见的,比如服务器回包:Cache-Control:max-age=600表示文件在本地应该缓存,且有效时长是600秒(从发出请求算起)。在接下来600秒内,如果有请求这个资源,浏览器不会发出HTTP请求,而是直接使用本地缓存的文件。

Last-Modified是标识文件在服务器上的最新更新时间。下次请求时,如果文件缓存过期,浏览器通过If-Modified-Since字段带上这个时间,发送给服务器,由服务器比较时间戳来判断文件是否有修改。如果没有修改,服务器返回304告诉浏览器继续使用缓存;如果有修改,则返回200,同时返回最新的文件。

Cache-Control通常与Last-Modified一起使用。一个用于控制缓存有效时间,一个在缓存失效后,向服务查询是否有更新。

Cache-Control还有一个同功能的字段:Expires。Expires的值一个绝对的时间点,如:Expires:Thu,10Nov201508:45:11GMT,表示在这个时间点之前,缓存都是有效的。

Expires是HTTP1.0标准中的字段,Cache-Control是HTTP1.1标准中新加的字段,功能一样,都是控制缓存的有效时间。当这两个字段同时出现时,Cache-Control是高优化级的。

Etag也是和Last-Modified一样,对文件进行标识的字段。不同的是,Etag的取值是一个对文件进行标识的特征字串。在向服务器查询文件是否有更新时,浏览器通过If-None-Match字段把特征字串发送给服务器,由服务器和文件最新特征字串进行匹配,来判断文件是否有更新。没有更新回包304,有更新回包200。Etag和Last-Modified可根据需求使用一个或两个同时使用。两个同时使用时,只要满足基中一个条件,就认为文件没有更新。

3. iOS webView利用NSURLProtocol实现离线缓存

        最近公司有一个需求,要对webView(UIWebView)实现缓存机制。即在无网条件下,打开webView页面,能读取到网页,有网情况下,缓存未过期也可以使用本地缓存,加快用户读取网页速度。

        实现缓存策略的方案有很多,为了保证有效,可控等多方面因素,本文采用NSURLProtocol来实现该需求,它的优势很多,楼主就不再累述了。

        关于NSURLProtocol,网上给出了很多资料,但很多方案都有缺陷,包括github上有star的项目,会遇到在特定情况下,网页加载不出来的问题,导致一直显示空白页。本文成功解决了这些问题,目前该项目已在线上稳定运行。

        写这篇文章,一方面为了自己,做一些整理,另一方面如果小伙伴,遇到类似需求后,不至于走太多弯路,所谓前人栽树,后人乘凉。废话不多说了,直接上内容。

首先关于URL Loading System

简单来说, URL Loading System 是iOS一系列网络请求类的集合,包括老的NSConnection和现在流行的NSURLSession,还包括一些请求认证的类,一个sessionConfig的类,还有关于处理请求缓存的类等,当然也包括NSURLProtocol类。

当我们需要拦截URL请求时,我们只要通过 - registerClass: 方法注册我们的NSURLProtocol类,然后去重写NSURLProtocol类中的方法,就能对我们发起的请求做处理。

NSURLProtocol 是一个抽象类,所以使用的时候必须定义一个它的子类:

需要拦截URL的控制器中注册该类:

记得在不需要的时候,及时关闭它:

注意一点:如果是基于 NSURLSession 进行的请求,注册的时候需要注册到 NSURLSessionConfiguration 中:

注册成功之后,需要子类CLURLSessionProtocol去实现抽象方法:

+ (BOOL)canInitWithRequest:(NSURLRequest*)request

此处可以拦截需要处理的URL,已经处理过的请求,需要标记请求是否被处理过,防止死循环

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request

这个方法用来统一处理请求 request 对象的,可以修改头信息,或者重定向。没有特殊需要,则直接return request;

如果要在这里做重定向以及头信息的时候注意检查是否已经添加,因为这个方法可能被调用多次,也可以在后面的方法中做。

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)a toRequest:(NSURLRequest*)b

判断网络请求是否一致,一致的话使用缓存数据。没需要就调用 super 的方法。

- (void)startLoading

这个方法作用很大,把当前请求的request拦截下来以后,在这个方法里面对这个request做各种处理,比如添加请求头,重定向网络,使用自定义的缓存等。

重点:需要标记已经处理过的 request:

- (void)stopLoading

取消流程

流程图:

4. iOS webView 怎么缓存

为了保证系统的流畅运行,有一部分运存是被冻结了的:
1、经常从屏幕底部上滑调出快捷栏,点击一键加速;
2、进入腾讯手机管家,点击空间清理,将系统文件以及缓存垃圾清理一下;
3、将不常用的软件卸载掉,可以移动的软件移动到内存卡安装,尽量释放系统空间提高稳定度;
4、必要的情况下可以操作清除清除所有数据解决,清除之前记得将手机数据备份好,然后点击设置--更多设置--恢复出厂设置--清除手机数据。

5. iOS web缓存策略以及手动清除缓存

当我们使用webview加载html资源时,本质上就是向服务器索取资源的http请求过程,如果我们不注意资源的缓存策略的话,就可能会造成这样那样的问题,比如:实时性要求较高的功能却老是走缓存不更新,有些基本不会变动的页面却又每次都重新去服务器拉请求。

iOS自带的缓存策略,提供了一个内存和磁盘混合的缓存,一共有7种缓存策略,使用较多的是其中的四种( 下方编号1,2,5,6 )

上面介绍了iOS自带的缓存控制 NSURLRequestCachePolicy ,也说到当 NSURLRequestCachePolicy 设为默认的 时,主要是根据http的缓存策略来决定是否使用缓存。

那么就简单的介绍一下,http的缓存控制和缓存校验。

在http中,控制缓存开关的字段有两个,Pragma和Cache-Control

Pragma有两个字段no-cache和expires,当pragma为no-cache时表示禁用缓存,expires的值是一个GMT时间,表示该缓存的有效时间。但是已经被逐步抛弃了,有些网站为了向下兼容还保留了这两个字段。

Cache-Control除了在响应中使用,在请求中也可以使用。

在请求中使用,Cache-Control可选的值有:

在响应中使用,Cache-Control可选的值有:

在缓存中,我们需要一个机制来验证缓存是否有效。比如服务器的资源更新了,客户端需要及时刷新缓存;又或者客户端的资源过了有效期,但服务器上的资源还是旧的,此时不需要重新发送。缓存校验就是用来解决这些问题的,在http1.1中,主要关注下 Last-Modified 和 etag 这两个字段。

服务端在返回资源时,会将该资源的最后更改时间通过 Last-Modified 字段返回给客户端。客户端下次请求时通过 If-Modified-Since 或者 If-UnModified-Since 带上 Last-Modified ,服务端检查该时间是否与服务器的最后修改时间一致:如果一致,则返回304状态码,不反悔资源;如果不一致,则返回200和修改后的资源,并带上新的时间。

单纯的以修改时间来判断还是有缺陷,比如文件的最后修改时间变了,但内容没变。对于这样的情况,我们可以使用etag来处理。
etag的方式是这样:服务器通过某个算法对资源进行计算,取得一串值(类似于文件的md5值),之后将该值通过etag返回给客户端,客户端下次请求时通过If-None-Match或If-Match带上该值,服务器对该值进行对比校验:如果一致则不要返回资源。

当我们的webview缓存到一定的峰值的时候,需要手动的清除一下wenview的缓存,方法如下:

找出web缓存的路径,清空该路径

webKit除了清除缓存的API

觉得有用,请帮忙点亮红心

Better Late Than Never!
努力是为了当机会来临时不会错失机会。
共勉!

6. iOS wkwebview怎么写localStorage

iOS中WKWebView,存在首次加载h5页面,h5页面中的js就拿不到localstorage了。

WKWebView localStorage 缓存很严重

HTML5在客户端存储数据的方式:cookie , localStorage, sessionStorage
cookie:只能存储少量的数据, 常用来存储账号密码等
localStorage : 没有时间限制的数据存储
sessionStorage : 针对一个 session 的数据存储, 当网页关闭时,数据也会被删除。

1.WKWebView设置localStorage
NSString * userContent = [NSString stringWithFormat:@"{"token": "%@", "userId": %@}", @"a1cd4a59-974f-44ab-b264-46400f26c849", @"89"];
// 设置localStorage
NSString *jsString = [NSString stringWithFormat:@"localStorage.setItem('userContent', '%@')", userContent];
// 移除localStorage
// NSString *jsString = @"localStorage.removeItem('userContent')";
// 获取localStorage
// NSString *jsString = @"localStorage.getItem('userContent')";
[self.webView evaluateJavaScript:jsString completionHandler:nil];

NSString * userContent = @"{"name": "Tom", "age": 10}"];
// 设置localStorage
NSString *jsString = [NSString stringWithFormat:@"localStorage.setItem('userContent', '%@')", userContent];
// 移除localStorage
// NSString *jsString = @"localStorage.removeItem('userContent')";
// 获取localStorage
// NSString *jsString = @"localStorage.getItem('userContent')";
[self.webView :jsString];
//清理掉所有的localStorage数据
//NSString *clearString = @"localStorage.clear()";

iOS wkwebview localstorage数据处理

WKWebView 在内存占用上优化的很多。但是在实践中发现bug:localstorage信息不一致。

A页面和B页面都存在 一个WKWebView。 在B页面使用localstorage保存信息。 回到A页面取不到最新的数据。

原因:

https://developer.apple.com/reference/webkit/wkwebviewconfiguration 中有个属性 processPool,描述是:The process pool from which to obtain the view’s Web Content process.

解决方法:

把config中的processPool变为单例共享

}

设置webview的配置 config.processPool = [NYWKWebView singleWkProcessPool];

在查询资料的过程中,发现了很多Local Storage的缺陷,有一篇关于Local Storage的 论文 可以参考。有以下几点:

webview 和 iframe 有什么区别?

webview是网页的原生载体,用于在原生环境中加载一个页面,iframe是网页的html载体,用于在网页中加载一个页面

7. [iOS]使用WKWebView遇到的问题总结

在使用WKWebView获取userAgent的时候, 如果要全局配置, 使所有的WKWebView都能生效, 我们可能的做法是在AppDelegate中来配置, 但是需要一个WKWebView实例对象, 所以可能是这么写的:

这样, 你会发现设置一直是无效的, 在回调里打印一下:

这时发现获取到的 info 字段为nil, 并且error信息如下:

个人猜测: 这是因为, WKWebView的evaluateJavaScript方法是异步执行的, 当WKWebView回调这个方法的时候, 其实例对象已经从内存中释放了, 所以导致回调出错.
我做了如下验证, 在回调方法里输出webView实例对象:

会发现输出是info有值, 而error为nil, webView有值, 又正常了, 有人说了,看样子不是这个问题! 真的么? 仔细想一下会发现, 在webView的方法回调闭包里使用了webView实例, 会发生什么? 对, 循环引用! webView实例此时不为nil, 这也验证了, 如果webView实例正常的话, 获取结果是不会有误的! 继续上面的验证, 我们弱引用一下:

这时, 会发现: info和webView都为nil, error值为上面那个错误!!!

这样, 基本验证出现这个问题的原因是: webView 提前释放了!

但是为了添加这个设置, 而将webView 设为全局变量, 仿佛有点得不偿失, 这时可以在使用webView的页面进行设置, 或者使用UIWebView替换:

在做JS与原生交互的时候, 使用下面方法注入的协议无效:

然后在js端使用的时候: 这里不需要传参数, 直接这么写的

这样, 没有响应js端的事件!!!
在代理方法中:

一直没有收到回调!!!
其实, 并不是注入协议失败, 这么使用也没问题, 问题就出在postMessage的参数上, 如果是带参数的:

这么写, 是完全没有问题的, 所以如果不需传参数的话, 可以这么写:

给一个空的字典, 就能正常交互了!!!

在WKWebView加载的HTML页面上, 如果长按会弹出一些选择框, 在文字上长按, 会弹出UIMenuController选择框:

而在图片上长按, 会弹出一个alertSheet:

这里可以保存图片到系统相册(如果有权限), 或者复制到剪切板. 但是这些需求并不是我们需要, 如何禁止这些行为呢?需要从JS入手, 只需要执行下面两句js即可:

可以在创建WKWebView的时候注入:

也可以在页面加载完成后的代理方法中执行:

在加载的HTML页面中, 无端出现一个广告的悬浮框:

打开之后是这样的:

而且只会在移动4G网络下才会出现, 其实这是移动的流量劫持, 强加的广告推广,目前网上有一些解决方式,常用的有:

其他的可参考这篇文章 iOS 客户端对于运营商劫持的一点点对抗方式


在联调的时候, 前端的同学改了一些东西, 例如页面的布局, 显示元素, 或者js方法, 而APP端没反应!!!

这是因为, WKWebView有缓存, 为了保证每次加载的都是最新的页面, 可以在加载的链接后面加上一个时间戳, 例如你的HTML地址为:

一般使用是这样的:

这样的话是有缓存, 加载一次之后, 再去加载也不是最新的页面, 可以这样使用:

这样每次加载的时候都会是最新的, 当然弊端就是, 每次都会耗费一些额外的流量.

在页面无导航的情况下,系统会自动调节滚动视图的contentInset,使其视图永远处于状态栏之下,但是如果我们想让滚动视图的Y坐标从屏幕顶端(状态栏)开始,我们都知道怎么修改,但是 WKWebView不是继承自UIScrollView 的,所以不能直接设置,可以这么写:

这个闪退发生在与 JS 进行交互,使用下面的方法注册协议时:

如果重复注册了相同名称的协议,就会发生闪退,所以在使用完webView的时候,一定要记得移除已注册的协议:

app首页使用 WKWebView 来承载的内容,在启动时,如果添加了引导页/广告页,这时如果有手势操作,例如在出现广告页时点击屏幕,就会闪退,并在控制台输出:

添加一个全局断点,调试发现崩溃信息为

查了些资料,没找到具体原因,但是了解到和 +load方法有关,我是在 +load 方法内初始化的广告页的信息,把这些放在 didFinishLaunchingWithOptions 进行初始化,就不会有这个问题;

解决:
将广告/引导页视图的初始化放在 didFinishLaunchingWithOptions 方法内。

8. iOS的webView如何实现html的离线缓存

这个实现难点在缓存图片上。html代码的缓存对你来说不是问题吧。基于这个前提,
下面这个方案是我自己做的,也具体在项目实现了。思路是这样的:
第1步、先获取html页面里所有图片地址。
方法一:离线获取获取到html代码。html代码你可以把他理解成是一个很长的字符串。通过正则表达式把这个html页面里的所有img标签url。如果是相对url,就加上host。如果是绝对url,就直接下载。这样这个页面里的所有图片路径都拿到了。
方法一的获取img标签url的正则表达式:
NSString *urlPattern = @"<img[^>]+?src=[\"']?([^>'\"]+)[\"']?";

方法二:通过webview和js 本地程序的交换,获取到html页面所有图片下载地址。

9. ios uiwebview 缓存文件在哪

顺便说下NSURLRequest对象,它有个cachePolicy属性,只要其值为的话,就不会访问缓存。可喜的是这种情况貌似只有在缓存里没取到,或是强制刷新时才可能出现。
实际上NSURLCache本身就有磁盘缓存功能,然而在iOS上,NSCachedURLResponse却被限制为不能缓存到磁盘(NSURLCacheStorageAllowed被视为)。
不过既然知道了原理,那么只要自己实现一个NSURLCache的子类,然后改写cachedResponseForRequest:方法,让它从硬盘读取缓存即可。

于是就开工吧。这次的demo逻辑比较复杂,因此我就按步骤来说明了。

先定义视图和控制器。
它的逻辑是打开应用时就尝试访问缓存文件,如果发现存在,则显示缓存完毕;否则就尝试下载整个网页的资源;在下载完成后,也显示缓存完毕。
不过下载所有资源需要解析HTML,甚至是JavaScript和CSS。为了简化我就直接用一个不显示的UIWebView载入这个页面,让它自动去发起所有请求。
当然,缓存完了还需要触发事件来显示网页。于是再提供一个按钮,点击时显示缓存的网页,再次点击就关闭。
顺带一提,我本来想用Google为例的,可惜它自己实现了HTML 5离线浏览,也就体现不出这种方法的意义了,于是只好拿网络来垫背。
Objective-c代码 收藏代码
#import <UIKit/UIKit.h>

@interface WebViewController : UIViewController <UIWebViewDelegate> {
UIWebView *web;
UILabel *label;
}

@property (nonatomic, retain) UIWebView *web;
@property (nonatomic, retain) UILabel *label;

- (IBAction)click;

@end

#import "WebViewController.h"
#import "URLCache.h"

@implementation WebViewController

@synthesize web, label;

- (IBAction)click {
if (web) {
[web removeFromSuperview];
self.web = nil;
} else {
CGRect frame = {{0, 0}, {320, 380}};
UIWebView *webview = [[UIWebView alloc] initWithFrame:frame];
webview.scalesPageToFit = YES;
self.web = webview;

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www..com/"]];
[webview loadRequest:request];
[self.view addSubview:webview];
[webview release];
}
}

- (void)addButton {
CGRect frame = {{130, 400}, {60, 30}};
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = frame;
[button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"我点" forState:UIControlStateNormal];
[self.view addSubview:button];
}

- (void)viewDidLoad {
[super viewDidLoad];

URLCache *sharedCache = [[URLCache alloc] initWithMemoryCapacity:1024 * 1024 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];

CGRect frame = {{60, 200}, {200, 30}};
UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
textLabel.textAlignment = UITextAlignmentCenter;
[self.view addSubview:textLabel];
self.label = textLabel;

if (![sharedCache.responsesInfo count]) { // not cached
textLabel.text = @"缓存中…";

CGRect frame = {{0, 0}, {320, 380}};
UIWebView *webview = [[UIWebView alloc] initWithFrame:frame];
webview.delegate = self;
self.web = webview;

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www..com/"]];
[webview loadRequest:request];
[webview release];
} else {
textLabel.text = @"已从硬盘读取缓存";
[self addButton];
}

[sharedCache release];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
self.web = nil;
label.text = @"请接通网络再运行本应用";
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.web = nil;
label.text = @"缓存完毕";
[self addButton];

URLCache *sharedCache = (URLCache *)[NSURLCache sharedURLCache];
[sharedCache saveInfo];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];

if (!web) {
URLCache *sharedCache = (URLCache *)[NSURLCache sharedURLCache];
[sharedCache removeAllCachedResponses];
}
}

- (void)viewDidUnload {
self.web = nil;
self.label = nil;
}

- (void)dealloc {
[super dealloc];
[web release];
[label release];
}

@end

10. ios wkwebview 是否有缓存

有缓存的
如果清理缓存
- (void)dealloc {
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
[self.webView removeObserver:self forKeyPath:@"title"];
[self clearCache];
}

/** 清理缓存的方法,这个方法会清除缓存类型为HTML类型的文件*/
- (void)clearCache {
/* 取得Library文件夹的位置*/
NSString *libraryDir = (NSLibraryDirectory,NSUserDomainMask, YES)[0];
/* 取得bundle id,用作文件拼接用*/
NSString *bundleId = [[[NSBundle mainBundle] infoDictionary]objectForKey:@"CFBundleIdentifier"];
/*
* 拼接缓存地址,具体目录为App/Library/Caches/你的APPBundleID/fsCachedData
*/
NSString *webKitFolderInCachesfs = [NSString stringWithFormat:@"%@/Caches/%@/fsCachedData",libraryDir,bundleId];

NSError *error;
/* 取得目录下所有的文件,取得文件数组*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// NSArray *fileList = [[NSArray alloc] init];
//fileList便是包含有该文件夹下所有文件的文件名及文件夹名的数组
NSArray *fileList = [fileManager contentsOfDirectoryAtPath:webKitFolderInCachesfs error:&error];
/* 遍历文件组成的数组*/
for(NSString * fileName in fileList){
/* 定位每个文件的位置*/
NSString * path = [[NSBundle bundleWithPath:webKitFolderInCachesfs] pathForResource:fileName ofType:@""];
/* 将文件转换为NSData类型的数据*/
NSData * fileData = [NSData dataWithContentsOfFile:path];
/* 如果FileData的长度大于2,说明FileData不为空*/
if(fileData.length >2){
/* 创建两个用于显示文件类型的变量*/
int char1 =0;
int char2 =0;

[fileData getBytes:&char1 range:NSMakeRange(0,1)];
[fileData getBytes:&char2 range:NSMakeRange(1,1)];
/* 拼接两个变量*/
NSString *numStr = [NSString stringWithFormat:@"%i%i",char1,char2];
/* 如果该文件前四个字符是6033,说明是Html文件,删除掉本地的缓存*/
if([numStr isEqualToString:@"6033"]){
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@/%@",webKitFolderInCachesfs,fileName]error:&error];
continue;
}
}
}
}

热点内容
防火门报配置错误是什么原因 发布:2024-10-05 20:20:47 浏览:886
移动宽带设置路由器怎么设置密码 发布:2024-10-05 20:03:30 浏览:105
微指令的编译方法有哪一些 发布:2024-10-05 19:02:10 浏览:885
android离线定位 发布:2024-10-05 18:36:40 浏览:858
ipad4密码忘记怎么办 发布:2024-10-05 18:36:07 浏览:237
黑莓加密天线 发布:2024-10-05 18:30:07 浏览:850
编程入行年龄 发布:2024-10-05 18:29:24 浏览:540
服务器地址访问不到 发布:2024-10-05 18:20:55 浏览:691
手机解锁忘记密码多少钱 发布:2024-10-05 18:14:25 浏览:787
linux乱码问题 发布:2024-10-05 18:00:25 浏览:544