美文网首页
HybridKit缓存及流量优化方案

HybridKit缓存及流量优化方案

作者: MaxWellPro | 来源:发表于2017-02-10 10:17 被阅读342次

    HybridKit缓存及流量优化方案

    年前,一直在忙项目上的事情也没时间写些东西,趁现在项目还处于空闲期,写下项目中遇到的优化方案。刚解决完发热问题,用户就开始反馈我们的app超级费流量,领导也非常重视。我们所用的图片服务器是又拍云支持尺寸裁剪,但是裁剪后的储存大小还是不可观。正好又拍云支持WebP图片格式,何不尝试下呢?

    导致费流量的原因

    • 1.HybridKit的UIWebView没有做缓存
    • 2.又拍云裁剪后的储存大小还是不够可观

    WebP

    WebP(发音 weppy,项目主页),是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8。根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些 PNG 文件经过其他压缩工具压缩之后,WebP 还是可以减少 28% 的文件大小。

    2010 年发布的 WebP 已经不算是新鲜事物了,在 Google 的明星产品如 Youtube、Gmail、Google Play 中都可以看到 WebP 的身影,而 Chrome 网上商店甚至已完全使用了 WebP。国外公司如 Facebook、ebay 和国内公司如腾讯、淘宝、美团等也早已尝鲜。目前 WebP 也在我厂很多的项目中得到应用,如腾讯新闻客户端、腾讯网、QQ空间等,同时也有一些针对 WebP 的图片格式转换工具,如 智图,iSparta 等。

    缓存方案

    SDWebImage

    SDWebImage是iOS开发者经常使用的一个开源框架,这个框架的主要作用是:一个异步下载图片并且支持缓存的UIImageView分类。相信各位iOS开发工程师都不会陌生。

    WebP集成方法

    1.CocoaPods

    pod 'SDWebImage/WebP'
    

    2.手动导入

    1.工程引入SDWebImage开源库;
    2.引入WebP.framework,下载地址:https://github.com/seanooi/iOS-WebP          
    3.让SDWebImage支持WebP,设置如下Build Settings -- Preprocessor Macros , add SD_WEBP=1
    

    NSURLCache

    • NSURLCache 为应用的 URL 请求提供了内存以及磁盘上的综合缓存机制,作为基础类库 URL 加载的一部分,任何通过 NSURLConnection 加载的请求都将被 NSURLCache 处理。
    • 网络缓存减少了需要向服务器发送请求的次数,同时也提升了离线或在低速网络中使用应用的体验。
    • 当一个请求完成下载来自服务器的回应,一个缓存的回应将在本地保存。下一次同一个请求再发起时,本地保存的回应就会马上返回,不需要连接服务器。NSURLCache
      会 自动 且 透明 地返回回应。
    • 为了好好利用 NSURLCache,你需要初始化并设置一个共享的 URL 缓存。在 iOS 中这项工作需要在 -application:didFinishLaunchingWithOptions: 完成
    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
      NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                           diskCapacity:20 * 1024 * 1024
                                                               diskPath:nil];
      [NSURLCache setSharedURLCache:URLCache];
    }
    

    NSURLProtocol

    NSURLProtocol可以拦截监听每一个URL Loading System中发出request请求,记住是URL Loading System中那些类发出的请求,也支持AFNetwoking,UIWebView发出的request。如果不是这些类发出的请求,NSURLProtocol就没办法拦截和监听了。

    之所以使用NSURLProtocol而不使用NSURLCache的原因是:NSURLProtocol可以拦截UIWebView发出的图片请求,如果检测到时又拍云图片链接,会把请求图片地址更改为WebP格式并使用SDWebImageDownloader来进行图片加载,这是NSURLCache不能实现的。

    实现

    1.在AppDelegate的-application:didFinishLaunchingWithOptions:方法中进行注册,这样NSURLProtocol才会正常工作。

    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        [BFNSURLProtocol registerClass:[BFNSURLProtocol class]];
        
         return YES;
    }
    

    2.WebView 图片请求替换

    webkit内核现在都不支持解析WebP格式的图片,这里主要采用的iOS系统的NSURLProtocol来替换其网络请求(不了解NSURLProtocol,可以动动自己勤劳的小手Google一下),再将网络回包数据进行转码成jpg或者png(为了透明度),再返回给webview进行渲染的。

    另外,NSURLProtocol会拦截全局的网络流量,为避免误伤,这里需要单独识别是否是WebView发起的请求,可以通过识别request中的UA是否包含”AppleWebKit”来实现。

    @implementation BFNSURLProtocol
    + (BOOL)canInitWithRequest:(NSURLRequest *)request {
        // 屏蔽非又拍云地址
        if ([[request.URL absoluteString] rangeOfString:@"upaiyun"].location == NSNotFound) {
            return NO;
        }
        //只处理http和https请求
        NSString *scheme = [[request URL] scheme];
        if (([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame || [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)) {
            //看看是否已经处理过了,防止无限循环
            if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
                return NO;
            }
            
            NSString *agent = [request valueForHTTPHeaderField:@"User-Agent"];
            // 只过滤UIWebview里边的加载图片请求
            if ([agent rangeOfString:@"AppleWebKit"].location != NSNotFound && [request.URL isImageURL]) {
                return YES;
            }
        }
        return NO;
    }
    

    敲黑板,划重点了!划重点了!划重点了!(重要的事情说三遍)

    这里可以接管所有WebView中需要替换的图片URL。

    下面,会自动调用startLoading方法,利用SDWebImageManager来加载WebP图片,不仅能实现WebP的省流量功能,还能将图片缓存在本地,下次加载同一图片地址的时候又达到了省流量的目的。耶!~

    - (void)startLoading {
        NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
        //标示改request已经处理过了,防止无限循环
        [NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
        NSString *URLString = [self.request.URL absoluteString];
        
        // 重定义请求地址 将format为jpg改为webp格式
        if ([URLString rangeOfString:@"format"].location == NSNotFound) {
            URLString = [[BFWebImageHelper imageStringToURL:URLString width:0 height:0] absoluteString];
        }
        else if ([URLString rangeOfString:@"format/jpg"].location != NSNotFound) {
            URLString = [[BFWebImageHelper imageStringToURL:URLString width:0 height:0] absoluteString];
        }
        else {
            self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];
            return;
        }
        
        NSURL *url = [NSURL URLWithString:URLString];
        
        [[SDWebImageManager sharedManager] downloadImageWithURL:url
                                                        options:SDWebImageAllowInvalidSSLCertificates
                                                       progress:nil
                                                      completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                                                          NSData *data;
                                                          // 是否以png结尾
                                                          if ([imageURL.absoluteString.lowercaseString hasSuffix:@".png"]) {
                                                              data = UIImagePNGRepresentation(image);
                                                          } else {
                                                              data = UIImageJPEGRepresentation(image, 1);
                                                          }
                                                          if (!self.client) {
                                                              return ;
                                                          }
                                                          [self.client URLProtocol:self didLoadData:data];
                                                          [self.client URLProtocolDidFinishLoading:self];
                                                      }];
    }
    
    - (void)stopLoading {
        [self.connection cancel];
        self.connection = nil;
    }
    
    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
        NSMutableURLRequest *mutableReqeust = [request mutableCopy];
        mutableReqeust = [self redirectHostInRequset:mutableReqeust];
        return mutableReqeust;
    }
    
    + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)equivalent toRequest:(NSURLRequest *)request; {
        return [super requestIsCacheEquivalent:equivalent toRequest:request];
    }
    
    + (NSMutableURLRequest *)redirectHostInRequset:(NSMutableURLRequest *)request {
        return request;
    }
    
    

    其他代理

    #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];
    }
    
    // 请求失败&错误的代理
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        [self.client URLProtocol:self didFailWithError:error];
    }
    

    PS:流程图
    流程图链接processon

    总结

    在使用了Webp之后,经QA同事的多次测试,同样一套步骤下比未优化前流量节省了47%,使用NSURLProtocol缓存方案后,在WebP优化基础上又优化了12%的流量。其实NSURLProtocol的功能远远不止这些,NSURLProtocol就是一个黑魔法,有兴趣的童鞋可以深入研究一下。今天就写到这里吧。

    相关文章

      网友评论

          本文标题:HybridKit缓存及流量优化方案

          本文链接:https://www.haomeiwen.com/subject/ejlgittx.html