- 在最初接觸數(shù)據(jù)緩存的時候 就看到多個博客提到可以用SDWebImage緩存圖片方式的思路去做數(shù)據(jù)緩存渗鬼,可是基本沒有看到去實踐的览露,而更多看到的使用sqlite的封裝FMDB去做緩存,也有用系統(tǒng)的NSURLCache譬胎,可表現(xiàn)的都很難擴展差牛,對請求緩存中的一些操作都很單一。也許我沒看到好的吧堰乔,如果有請留言給我謝謝偏化。
- 先說說一些緩存中的 誤區(qū):看到很多剛接觸緩存這塊的開發(fā)者認為 網(wǎng)絡狀態(tài)是緩存的第一判斷條件,大概是有網(wǎng)絡就重新請求數(shù)據(jù)浩考,無網(wǎng)絡就使用緩存夹孔。這么認為的就是進入誤區(qū)了。就說一個例子析孽,有網(wǎng)絡搭伤,但服務器掛了,怎么辦袜瞬。顯示你返回請求失敗的異常頁面嗎怜俐?有人說了 請求失敗再使用緩存唄。咱們做APP的 是不是要考慮用戶的使用體驗啊邓尤。請求失敗了拍鲤,都啥時候了贴谎,黃花菜都涼了。
- 大家可以看看幾個主流APP 的緩存顯示方式“ 網(wǎng)易新聞”,“今日頭條”,“ 微博” 季稳。在有緩存的狀態(tài)下擅这, 這幾款APP都是 優(yōu)先顯示緩存,同時有的進行自動下拉刷新操作景鼠,有的給你提示仲翎,有多少條數(shù)據(jù)更新,請進行下拉刷新操作 铛漓。下拉刷新就是重現(xiàn)請求溯香,請求的結果有很多狀態(tài),與界面操作有關就幾種浓恶,請求成功替換顯示的數(shù)據(jù)玫坛,請求超時給提示,請求失敗給提示包晰∈疲可以看出 緩存是和請求分不開的(由于GET請求一般用來查詢數(shù)據(jù),POST請求一般是發(fā)大量數(shù)據(jù)給服務器處理(變動性比較大)因此一般只對GET請求進行緩存杜窄,而不對POST請求進行緩存)肠骆。
- 正確的緩存的使用策略 是在有緩存的狀態(tài)下,使用緩存塞耕,沒有緩存去請求,請求的成功后的數(shù)據(jù)刷新界面嘴瓤,并覆蓋原緩存文件扫外。結合網(wǎng)絡請求,還要給出不使用緩存的狀態(tài)廓脆,和使用緩存的狀態(tài).在實際運用中可以能還有別的需求筛谚,比如上拉加載的狀態(tài),離線緩存的狀態(tài)停忿,所以請求的種類多樣驾讲,緩存的使用策略的狀態(tài)也要多樣。
- 根據(jù)此緩存策略 封裝的 請求框架 ZBNetworking
ZBNetworking
優(yōu)點:
1.請求類型豐富/ * GET請求 // * POST請求 // * PUT請求 // * PATCH請求 // * DELETE請求 // * 上傳請求 // * 下載請求 /
2.低耦合席赂,易擴展吮铭。
3.通過Block配置信息,代碼緊湊;
4.有緩存文件過期機制默認一周
5.顯示緩存大小/個數(shù)颅停,全部清除緩存/單個文件清除緩存/按時間清除緩存/按路徑清除緩存方法多樣并且都可以自定義路徑可擴展性強
6.有緩存鍵過濾功能
7.離線下載功能
8.多種請求緩存類型的判斷谓晌。也可不遵循,自由隨你定癞揉。
/** 重新請求 ,不讀取緩存纸肉,重新請求*/
ZBRequestTypeRefresh,
/** 有緩存,讀取緩存 無緩存溺欧,重新請求*/
ZBRequestTypeCache,
/** 加載更多 ,不讀取緩存,重新請求*/
ZBRequestTypeRefreshMore,
/** 加載更多 ,有緩存,讀取緩存 無緩存柏肪,重新請求*/
ZBRequestTypeCacheMore,
/** 詳情 ,有緩存,讀取緩存 無緩存姐刁,重新請求*/
ZBRequestTypeDetailCache,
/** 自定義 ,有緩存,讀取緩存 無緩存,重新請求*/
ZBRequestTypeCustomCache
9.可見的緩存文件
AFNetworking 不多介紹了 在使用OC開發(fā)的烦味,很多都用這個聂使。封裝了一個緩存管理類ZBCacheManager,主要參照了SDWebImage的SDImageCache磁盤存儲的思路,SDWebImage的存儲性能大家應該不用懷疑拐叉,但也要進行大量的改動岩遗,畢竟SDImageCache 只是緩存圖片的。不管用那種請求方法 緩存文件都會存儲到沙盒 /Library/Caches/ZBKit/AppCache 路徑下 凤瘦。
有一點要注意宿礁,請求下來的數(shù)據(jù)格式 是二進制的 因為緩存也是二進制數(shù)據(jù) 。所以設置了responseSerializer = [AFHTTPResponseSerializer serializer];不能更改蔬芥。
- 運行流程如下圖
請求示例 具體看demo 梆靖。
2017-8-21 重構了此庫 現(xiàn)在默認為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:@""];
}
}];
//取消對應的網(wǎng)絡請求
[ZBRequestManager cancelRequest: list_URL completion:^(NSString *urlString){
//如果請求成功 或 讀緩存 會返回null 無法取消返吻。請求未完成的會取消并返回對應url
//NSLog(@"取消對應url:%@ ",urlString);
}];
-
其他的一些功能 顯示緩存大小 ,緩存?zhèn)€數(shù)。按時間清除緩存乎婿,手動清理緩存测僵,自定義清除某個路徑文件,清除某個單獨key的緩存等功能谢翎,
9075F331-0F70-44B8-8405-E067D1EE0A28.png
離線緩存/下載
- 下面要說離線緩存功能捍靠。不知道該功能的:可以下個今日頭條/我的 /離線 功能看看 一般新聞類的APP 大多有該功能。
要做離線緩存功能:必須要有先決條件森逮,就是一定不要多個緩存路徑 榨婆,特別是圖片的緩存功能,存儲路徑要統(tǒng)一褒侧。我的demo中圖片請求現(xiàn)在使用的是SDWebImage良风,其實也可以用別的(如YYWebImage)。只要你用的圖片請求框架 在數(shù)據(jù)列表顯示的圖片 和 在離線下載 的圖片 用的是一個緩存就可以闷供,SDWebImage 因為用了MD5編碼烟央,確保了每個URL對應的緩存唯一性。這里有個問題这吻,有很多應用有輪播視圖的功能 吊档,而大多數(shù)封裝好輪播都是自己進行請求并緩存到自定義的緩存路徑,這個要處理一下唾糯,最好統(tǒng)一為一個圖片請求框架怠硼,緩存路徑也就一樣了鬼贱。
好了 我們的列表圖片包括輪播圖片 緩存 都在SDWebImage 的默認緩存路徑里了,
而我們的json 數(shù)據(jù) 已經(jīng)緩存到/Library/Caches/ZBKit/AppCache 路徑下 香璃。下面開始 實現(xiàn)離線功能
-
其實離線緩存并不是一個 高深的技術这难,還是緩存。正常我們?yōu)g覽一個頁面緩存這個頁面的數(shù)據(jù)葡秒。而離線就是同時請求多個頁面并緩存下來姻乓。
F7B3FE2C-FE73-4D9E-A394-104C22FAA391.png
思路就是 每個頻道就是一個url,把你要緩存的 url 放到一個數(shù)組里 進行遍歷請求。ZBRequestManager已經(jīng)封裝好了 離線的方法
- (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=@"已經(jīng)下載了";
} 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:@""];
}
}];
}
點擊 Github地址 下載
結尾:水平有限,代碼也很爛学少,一直在努力學習中剪个,大家多多包涵。如果你喜歡這個輪子版确,請給個star扣囊,這是對作者最大的鼓勵和支持,拜謝H蘖啤G中!假如你有更好的想法或方案請留言吓蘑!
下篇文章:設計一個簡單的圖片緩存器