通过重写NSURLProtocol实现UIWebView的数据缓存

由于公司项目是混合开发,基本上全部是通过UIWebView加载的,虽然这样减轻了客户端的工作量,但是,不可避免会遇到网络差,服务器不稳定等的情况,在这种情况下,webView的加载效果就很差了,严重影响客户体验,于是产品经理要求研究客户端的优化、缓存问题。网上是找了一个NSURLProtocol的缓存的思路的,但是到今天实际上手操作的时候就遇到问题了。今天下午就好好研究了一下,终于把问题搞定了。下面开始正文:
一、NSURLProtocol 简介
其实网上关于NSURLProtocol的介绍已经很好很详细了,我就不做过多介绍了,说最主要的一点,你可以通过继承NSURLProtocol,自定义一个子类,然后可以轻松的重新定义苹果的URL加载系统 (URL Loading System)的行为。通过拦截,可以实现各种操作
二、实现的主要步骤
首先,要在AppDelegate.m的- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions 里面进行注册
[NSURLProtocol registerClass:[MyConnectionURLProtocol class]];
然后,重写NSURLProtocol的方法
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
每个请求都会进入这个方法,返回YES表示接管系统请求,自己进行相关操作,返回NO,表示,默认系统的请求。我们可以在这个方法里面判断自己需要做处理的连接,可以根据实现webView缓存,判断要加载的资源在本地是否存在,如果存在,接管系统请求,加载本地资源

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {

    /* 
        防止无限循环,因为一个请求在被拦截处理过程中,也会发起一个请求,这样又会走到这里,如果不进行处理,就会造成无限循环
     */
    if ([NSURLProtocol propertyForKey:protocolKey inRequest:request]) {
        return NO;
    }
    NSString * url = request.URL.absoluteString;
    // 如果url已http或https,开头,则进行拦截处理,否则不处理
    if ([url hasPrefix:@"http"] || [url hasPrefix:@"https"]) {
        //判断要加载的资源本地是否存在
        if ([MyConnectionURLProtocol localResourceIsExistWith:request]) {
            return YES;
        } else {
            return NO;
        }

    }
    return NO;
}

这是相应的判断。
当上面的操作返回为YES的时候,才会继续下面的操作。
- (void)startLoading重写该方法,需要在该方法中发起一个请求,对于NSURLConnection来说,就是创建一个NSURLConnection,对于NSURLSession,就是发起一个NSURLSessionTask,在这里可以进行相关的操作,加载本地资源

 NSMutableURLRequest * request = [self.request mutableCopy];
    // 表示该请求已经被处理,防止无限循环
    [NSURLProtocol setProperty:@(YES) forKey:protocolKey inRequest:request];
    NSLog(@"URL %@",request.URL.absoluteString);
    NSString *str = request.URL.path;
    NSString *Regex = nil;
    if ([str.pathExtension isEqualToString:@"js"])
    {
        Regex = [MyConnectionURLProtocol addLocalFileReturnRex:@"js"];

    }else if ([str.pathExtension isEqualToString:@"css"])
    {
        Regex = [MyConnectionURLProtocol addLocalFileReturnRex:@"css"];

    }else if ([str.pathExtension isEqualToString:@"jpg"]||[str.pathExtension isEqualToString:@"png"])
    {
        Regex = [MyConnectionURLProtocol addLocalFileReturnRex:@"img"];

    }

    [MyConnectionURLProtocol rexString:str rex:Regex success:^(NSString *matchStr) {

        NSArray *fileArr = [matchStr componentsSeparatedByString:@"."];
        [self addFiledToClient:matchStr MIMEType:fileArr.lastObject];


    } faild:^{

        self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
    }];

重写- (void)stopLoading该方法,需要停止响应的请求

- (void)stopLoading {
    [self.connection cancel];
}

此外,还要重写+ (NSURLRequest )canonicalRequestForRequest:(NSURLRequest )request该方法,可以增加或修改请求头等操作,主要是改变request的操作

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

    // 修改了请求的头部信息
    NSMutableURLRequest *mutableReqeust = [request mutableCopy];
    mutableReqeust = [self redirectHostInRequset:mutableReqeust];
    return mutableReqeust;
}

三、实现NSURLConnectionDataDelegate协议代理方

#pragma mark - NSURLConnectionDelegate

- (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];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
}

以上操作就可以实现UIWebView的缓存,谢谢观看

你可能感兴趣的:(iOS技术问题,uiwebview)