美文网首页自己尝试等装一下恩美第二个APP项目小知识点
iOS请求缓存、离线缓存 框架 ZBNetworking

iOS请求缓存、离线缓存 框架 ZBNetworking

作者: _Andy_ | 来源:发表于2017-03-15 10:50 被阅读657次
    • 在最初接触数据缓存的时候 就看到多个博客提到可以用SDWebImage缓存图片方式的思路去做数据缓存,可是基本没有看到去实践的,而更多看到的使用sqlite的封装FMDB去做缓存,也有用系统的NSURLCache,可表现的都很难扩展,对请求缓存中的一些操作都很单一。也许我没看到好的吧,如果有请留言给我谢谢。
    • 先说说一些缓存中的 误区:看到很多刚接触缓存这块的开发者认为 网络状态是缓存的第一判断条件,大概是有网络就重新请求数据,无网络就使用缓存。这么认为的就是进入误区了。就说一个例子,有网络,但服务器挂了,怎么办。显示你返回请求失败的异常页面吗?有人说了 请求失败再使用缓存呗。咱们做APP的 是不是要考虑用户的使用体验啊。请求失败了,都啥时候了,黄花菜都凉了。
    • 大家可以看看几个主流APP 的缓存显示方式“ 网易新闻”,“今日头条”,“ 微博” 。在有缓存的状态下, 这几款APP都是 优先显示缓存,同时有的进行自动下拉刷新操作,有的给你提示,有多少条数据更新,请进行下拉刷新操作 。下拉刷新就是重现请求,请求的结果有很多状态,与界面操作有关就几种,请求成功替换显示的数据,请求超时给提示,请求失败给提示。可以看出 缓存是和请求分不开的(由于GET请求一般用来查询数据,POST请求一般是发大量数据给服务器处理(变动性比较大)因此一般只对GET请求进行缓存,而不对POST请求进行缓存)。
    • 正确的缓存的使用策略 是在有缓存的状态下,使用缓存,没有缓存去请求,请求的成功后的数据刷新界面,并覆盖原缓存文件。结合网络请求,还要给出不使用缓存的状态,和使用缓存的状态.在实际运用中可以能还有别的需求,比如上拉加载的状态,离线缓存的状态,所以请求的种类多样,缓存的使用策略的状态也要多样。
    • 根据此缓存策略 封装的 请求框架 ZBNetworking

    ZBNetworking

    优点:
    1 低耦合,易扩展。(这个下篇文章体现)
    2.有缓存文件过期机制 默认一周(以最后缓存文件修改时间为准)
    3.显示缓存大小/个数,全部清除缓存/单个文件清除缓存/按时间清除缓存 方法多样 并且都可以自定义路径 可扩展性强
    4.离线下载功能
    5.多种请求类型的判断。也可不遵循,自由随你定。

        /** 重新请求 ,不读取缓存,重新请求*/
        ZBRequestTypeRefresh,
        /** 有缓存,读取缓存 无缓存,重新请求*/
        ZBRequestTypeCache,
        /** 加载更多 ,不读取缓存,重新请求*/
        ZBRequestTypeRefreshMore,
        /** 加载更多 ,有缓存,读取缓存 无缓存,重新请求*/
        ZBRequestTypeCacheMore,
        /** 详情    ,有缓存,读取缓存 无缓存,重新请求*/
        ZBRequestTypeDetailCache,
        /** 自定义  ,有缓存,读取缓存 无缓存,重新请求*/
        ZBRequestTypeCustomCache
    

    6.可见的缓存文件


    ZBNetworking_cache_gif.gif

    AFNetworking 不多介绍了 在使用OC开发的,很多都用这个。封装了一个缓存管理类ZBCacheManager,主要参照了SDWebImage的SDImageCache磁盘存储的思路,SDWebImage的存储性能大家应该不用怀疑,但也要进行大量的改动,毕竟SDImageCache 只是缓存图片的。不管用那种请求方法 缓存文件都会存储到沙盒 /Library/Caches/ZBKit/AppCache 路径下 。
    有一点要注意,请求下来的数据格式 是二进制的 因为缓存也是二进制数据 。所以设置了responseSerializer = [AFHTTPResponseSerializer serializer];不能更改。

    • 运行流程如下图
    F0C2D948-9691-40B5-8251-554FB85A84EA.png

    请求示例 具体看demo 。
    2017-8-21 重构了此库 现在默认为Refresh (重新请求,不读缓存)。

    [ZBRequestManager requestWithConfig:^(ZBURLRequest *request){
            request.urlString=list_URL;
            request.methodType=ZBMethodTypeGET;//默认为GET
            request.apiType=ZBRequestTypeCache;//默认为ZBRequestTypeRefresh
        }  success:^(id responseObj,apiType type){
            if (type==ZBRequestTypeRefresh) {
                  //下拉结束刷新
            }  
            if (type==ZBRequestTypeLoadMore) {
                // 上拉结束刷新
            }
                 //请求成功
            
        } failed:^(NSError *error){
            if (error.code==NSURLErrorCancelled)return;
            if (error.code==NSURLErrorTimedOut){
                [self alertTitle:@"请求超时" andMessage:@""];
            }else{
                [self alertTitle:@"请求失败" andMessage:@""];
            }
        }];
    
    //取消对应的网络请求
     [ZBRequestManager cancelRequest: list_URL completion:^(NSString *urlString){
            //如果请求成功 或 读缓存 会返回null 无法取消。请求未完成的会取消并返回对应url
            //NSLog(@"取消对应url:%@ ",urlString);
        }];
    
    • 其他的一些功能 显示缓存大小 ,缓存个数。按时间清除缓存,手动清理缓存,自定义清除某个路径文件,清除某个单独key的缓存等功能,


      9075F331-0F70-44B8-8405-E067D1EE0A28.png

    离线缓存/下载

    • 下面要说离线缓存功能。不知道该功能的:可以下个今日头条/我的 /离线 功能看看 一般新闻类的APP 大多有该功能。
      要做离线缓存功能:必须要有先决条件,就是一定不要多个缓存路径 ,特别是图片的缓存功能,存储路径要统一。我的demo中图片请求现在使用的是SDWebImage,其实也可以用别的。只要你用的图片请求框架 在数据列表显示的图片 和 在离线下载 的图片 用的是一个缓存就可以,SDWebImage 因为用了MD5编码,确保了每个URL对应的缓存唯一性。这里有个问题,有很多应用有轮播视图的功能 ,而大多数封装好轮播都是自己进行请求并缓存到自定义的缓存路径,这个要处理一下,这里我选用了一个codingZero 大神 XRCarouselView 的轮播视图进行修改,(为什么选择这个,因为他写好简单,代码好少,好修改)。
      先把其他的请求/创建/存储缓存方法都删掉
      然后找到 - (void)downloadImages:(int)index 方法 进行修改
    //使用SDWebImage方法替换内部实现
    - (void)downloadImages:(int)index {
        
        [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:_imageArray[index]] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize){
            
        } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType,BOOL finished,NSURL *imageURL){
            if (image) {
                self.images[index] = image;
                //如果下载的图片为当前要显示的图片,直接到主线程给imageView赋值,否则要等到下一轮才会显示
                if (_currIndex == index) [_currImageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
            }
        }];
    }
    
    
    C2A147C7-8F29-490F-82CE-62810491A150.png

    好了 我们的列表图片包括轮播图片 缓存 都在SDWebImage 的默认缓存路径里了,因为XRCarouselView 我并没有征得作者的同意更改,所以demo中就不使用了,主要让大家 知道 图片的请求与存储要统一
    而我们的json 数据 已经缓存到/Library/Caches/ZBKit/AppCache 路径下 。下面开始 实现离线功能

    • 其实离线缓存并不是一个 高深的技术,还是缓存。正常我们浏览一个页面缓存这个页面的数据。而离线就是同时请求多个页面并缓存下来。


      F7B3FE2C-FE73-4D9E-A394-104C22FAA391.png

    思路就是 每个频道就是一个url,把你要缓存的 url 放到一个数组里 进行遍历请求。ZBRequestManager已经封装好了 离线的方法

    - (void)requestOffline:(NSMutableArray *)offlineArray{
    
        [ZBRequestManager sendBatchRequest:^(ZBBatchRequest *batchRequest){
             for (NSString *urlString in offlineArray) {
                ZBURLRequest *request=[[ZBURLRequest alloc]init];
                request.urlString=urlString;
                [batchRequest.urlArray addObject:request];
            }
        }  success:^(id responseObj,apiType type){
    
                NSLog(@"添加了几个url请求  就会走几遍");
                NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObj options:NSJSONReadingMutableContainers error:nil];
                NSArray *array=[dict objectForKey:@"videos"];
                for (NSDictionary *dic in array) {
                    DetailsModel *model=[[DetailsModel alloc]init];
                    model.thumb=[dic objectForKey:@"thumb"]; //找到图片的key
                    [self.imageArray addObject:model];
                    
                    //使用SDWebImage 下载图片
                    BOOL isKey=[[SDImageCache sharedImageCache]diskImageExistsWithKey:model.thumb];
                    if (isKey) {
                        self.offlineView.progressLabel.text=@"已经下载了";
                    } else{
                        
                        [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:model.thumb] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize){
                            
                            NSLog(@"%@",[self progressStrWithSize:(double)receivedSize/expectedSize]);
                            
                            self.offlineView.progressLabel.text=[self progressStrWithSize:(double)receivedSize/expectedSize];
                            
                            self.offlineView.pv.progress =(double)receivedSize/expectedSize;
                            
                        } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType,BOOL finished,NSURL *imageURL){
                            
                            NSLog(@"单个图片下载完成");
                            self.offlineView.progressLabel.text=nil;
                            
                            self.offlineView.progressLabel.text=[self progressStrWithSize:0.0];
                            
                            self.offlineView.pv.progress = 0.0;
                            
                            [self.tableView reloadData];
                            //让 下载的url与模型的最后一个比较,如果相同证明下载完毕。
                            NSString *imageURLStr = [imageURL absoluteString];
                            NSString *lastImage=[NSString stringWithFormat:@"%@",((DetailsModel *)[self.imageArray lastObject]).thumb];
                            
                            if ([imageURLStr isEqualToString:lastImage]) {
                                NSLog(@"下载完成");
                                
                                [self.offlineView hide];//取消下载进度视图
                                [self alertTitle:@"下载完成"andMessage:@""];
                                 self.imageArray=nil;
                            }
                            
                            if (error) {
                                NSLog(@"下载失败");
                            }
                        }];
                        
                    }
                    
                }
    
        } failed:^(NSError *error){
            if (error.code==NSURLErrorCancelled)return;
            if (error.code==NSURLErrorTimedOut){
                [self alertTitle:@"请求超时" andMessage:@""];
            }else{
                [self alertTitle:@"请求失败" andMessage:@""];
            }
        }];
    }
    
    cache

    点击 Github地址 下载

    结尾:水平有限,代码也很烂,一直在努力学习中,大家多多包涵。如果你喜欢这个轮子,请给个star,这是对作者最大的鼓励和支持,拜谢!!!假如你有更好的想法或方案请留言!

    下篇文章:设计一个简单的图片缓存器

    相关文章

      网友评论

      • 哈哈大笑呼呼呼呼:不错的值得学习,楼主:+1:
      • 谁能叫我布朗:写的很清晰,期待持续更新
      • d926abd044b1:楼主,我把问题提交到了github上,帮忙看一下吧
        _Andy_:https://github.com/Suzhibin/ZBNetworking/issues/4
      • LightReason:看了源码,写的很好啊,怎么start辣么少
        _Andy_::smile: 谢谢支持
      • d926abd044b1:楼主,github上有了个issue,帮忙看下吧
      • d926abd044b1:post方法直接传递json数据,应该怎么传递
        _Andy_:@Chaos_9216 谢谢 支持, 就是自己用 ,也没想着推广
        d926abd044b1:牛逼,我昨晚还费了老大劲自己加进去个方法,丢人了! 这么好使的一个框架 我就不明白星星怎么就少了点...
        _Andy_:post请求参数传递json

        [ZBRequestManager requestWithConfig:^(ZBURLRequest * request){
        request.URLString = @“URL”;
        request.methodType = ZBMethodTypePOST;
        request.requestSerializerType == ZBJSONRequestSerializer
        request.parameters = @ {
        @“platformId”:@“225”,
        @“timezone”:@“Asia / Shanghai”};
        }success:nil failure:nil];
      • d926abd044b1:楼主,github上提了个问题,可以看一下吗?
      • PGOne爱吃饺子:楼主,你的这个硬盘缓存的时候,这个key值是怎么生成和判断的,看了一下你的代码没有怎么明白
        _Andy_:@PGOne爱吃饺子 cachePathForKey 调的这个。
        PGOne爱吃饺子:楼主,你的这个对key值用MD5加密的方法好像没有调用啊
        你看一下这个方法 diskCachePathForKey ,全局搜索一下
        _Andy_:key值是 请求的url地址 在存取的时候 用MD5加密
      • PGOne爱吃饺子:楼主,你好,请问你知道图片缓存的路径是在哪里么
        PGOne爱吃饺子:@大苏Andi 好的 谢谢啊
        _Andy_:如果你用的是SDwebImage 在 Library/Caches/default/
      • PGOne爱吃饺子:楼主,你好,请问你这个demo有什么用,相比AF和SD有什么好处啊
        PGOne爱吃饺子:@大苏Andi 好的,我仔细研究一下你的框架,谢谢啊
        _Andy_:增加了数据缓存 ,离线下载。
        AF 的缓存是内存缓存,而且需要自己实现。在关闭APP 内存会清空 ,重新启动APP 都是重新请求。 ZBNetworking增加磁盘缓存,并且提供了多样化的清除缓存策略。
        SD 是很好的网络图片请求,可以配合ZBNetworking做离线下载。当然不仅限如此,比如 你某个图 要更换图片,但是不换图片URL 。都可以使用
      • 吖几角:厉害
        _Andy_:谢谢支持:relaxed:

      本文标题:iOS请求缓存、离线缓存 框架 ZBNetworking

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