版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dolacmeng/article/details/80681771
一.项目需求
项目中有个海报生成功能,使用UIWebView加载一些网页,因为海报使用率比较高,有时加载网页比较慢会影响用户体验,因此我们在APP启动后,将一些固定资源,如css、图片等,先缓存到本地。加载网页时,通过NSURLProtocol,优先使用本地的文件,以加快网页加载速度。
二.Demo:百度首页logo改为本地图片
下面demo实现,加载baidu首页,百度logo图片改用本地缓存好的图片代替。
(一)获取到所有请求
(1)新建JXURLProtocol类,继承自NSURLProtocol
@interface JXURLProtocol : NSURLProtocol
@end
(2)在JXURLProtocol.m中实现+ (BOOL)canInitWithRequest:方法,打印出所有请求:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
NSString *urlString = request.URL.absoluteString;
NSLog(@"%@",urlString);
return NO;
}
(3)在加载网页前,注册JXURLProtocol类:
[NSURLProtocol registerClass:[JXURLProtocol class]];
(二)拦截请求
(1)我们需要拦截百度图片url的请求,即在canInitWithRequest:方法中,根据url判断,如果是百度logo图的url(https://m.baidu.com/static/index/plus/plus_logo.png),返回YES,表示不发出请求而是自己处理:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
NSString *urlString = request.URL.absoluteString;
NSLog(@"%@",urlString);
//此处防止重复拦截(与后面的三(3)呼应)
if ([NSURLProtocol propertyForKey:@"JXProtocol" inRequest:request]) {
return NO;
}
if ([urlString isEqualToString:@"https://m.baidu.com/static/index/plus/plus_logo.png"]) {
return YES;
}
return NO;
}
(2)返回YES后,会调用方法:
- (void)startLoading{}
(三)返回本地图片
(1)根据url返回本地图片:
- (NSData*)imageDataWithUrl:(NSURL*)url{
if ([url.absoluteString isEqualToString:@"https://m.baidu.com/static/index/plus/plus_logo.png"]) {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"long" ofType:@"png"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
return data;
}
return nil;
}
(2)尝试获取本地图片,获取成功后,构建响应头,回调请求成功。
- (void)startLoading{
NSData *imageData = [self imageDataWithUrl:self.request.URL];
if (imageData) {
//获取本地图片成功
//构建请求头
NSString *mimeType = @"image/jpeg";
NSMutableDictionary *header = [NSMutableDictionary dictionary];
NSString *contentType = [mimeType stringByAppendingString:@";chartset=UTF-8"];
header[@"Content-type"] = contentType;
header[@"Content-Length"] = [NSString stringWithFormat:@"%ld",imageData.length];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
statusCode:200 HTTPVersion:@"1.1" headerFields:header];
//回调
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
[self.client URLProtocol:self didLoadData:imageData];
[self.client URLProtocolDidFinishLoading:self];
}else{
//获取本地图片失败
}
}
(3)如果获取本地图片失败,设置标志防止重复拦截,并重新构建请求:
- (void)startLoading{
NSData *imageData = [self imageDataWithUrl:self.request.URL];
if (imageData) {
//获取本地图片成功
//...
}else{
[NSURLProtocol setProperty:@(YES) forKey:@"JXProtocol" inRequest:self.request];
NSMutableURLRequest *newRequset = [self.request mutableCopy];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionTask *task = [session dataTaskWithRequest:newRequset];
[task resume];
}
}
(四)实现必须的方法:
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
- (void)stopLoading{
}
#pragma mark- NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
(五)运行APP,百度的logo已经换成本地的long.png,小恐龙了:
完整代码:https://github.com/dolacmeng/NSURLProtocolDemo
更多资料:https://developer.apple.com/documentation/foundation/url_loading_system