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

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

ZBNetworking

优点:
1.请求类型丰富/ * GET请求 // * POST请求 // * PUT请求 // * PATCH请求 // * DELETE请求 // * 上传请求 // * 下载请求 /
2.低耦合,易扩展。
3.通过Block配置信息,代码紧凑;
4.有缓存文件过期机制默认一周
5.显示缓存大小/个数,全部清除缓存/单个文件清除缓存/按时间清除缓存/按路径清除缓存方法多样并且都可以自定义路径可扩展性强
6.有缓存键过滤功能
7.离线下载功能
8.多种请求缓存类型的判断。也可不遵循,自由随你定。

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

9.可见的缓存文件


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,其实也可以用别的(如YYWebImage)。只要你用的图片请求框架 在数据列表显示的图片 和 在离线下载 的图片 用的是一个缓存就可以,SDWebImage 因为用了MD5编码,确保了每个URL对应的缓存唯一性。这里有个问题,有很多应用有轮播视图的功能 ,而大多数封装好轮播都是自己进行请求并缓存到自定义的缓存路径,这个要处理一下,最好统一为一个图片请求框架,缓存路径也就一样了。
C2A147C7-8F29-490F-82CE-62810491A150.png

好了 我们的列表图片包括轮播图片 缓存 都在SDWebImage 的默认缓存路径里了,
而我们的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,这是对作者最大的鼓励和支持,拜谢!!!假如你有更好的想法或方案请留言!

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,595评论 4 59
  • 生活、工作中,谁都会遇上与别人起冲突的糟心事儿。有的人,选择以暴力的方式强行解决,枪杆子底下出政权就是这个思...
    諪諪_0c2f阅读 152评论 0 0
  • 初遇Whizzer A15,让笔者感兴趣的是老汪略带逗趣的微博以及A15那吸人眼球的造型。如果说一个熊孩子为了追求...
    数码畅想阅读 287评论 0 0