iOS開(kāi)發(fā)中網(wǎng)絡(luò)緩存蘋(píng)果已經(jīng)提供了比較好用的
NSURLCache
類,但是只支持GET
請(qǐng)求徘六,所以拋去原生的網(wǎng)絡(luò)緩存類八拱,就想寫(xiě)一個(gè)較為好用的網(wǎng)絡(luò)緩存封裝议双。
本文是基于數(shù)據(jù)庫(kù)FMDB
來(lái)完成實(shí)現(xiàn)的房维,并無(wú)技術(shù)深度占贫,只當(dāng)作為大家共同進(jìn)步道路上的一個(gè)思路娘扩。如Demo
中有任何疑問(wèn)同诫,大家可以提問(wèn)哈粤策。
實(shí)現(xiàn)思路由看了標(biāo)哥的HYBNetworking源碼提供,標(biāo)哥是在本地建文件夾實(shí)現(xiàn)的緩存剩辟,我是基于數(shù)據(jù)庫(kù)掐场,都是一樣的道理。
依賴的三方庫(kù)
首先贩猎,實(shí)現(xiàn)網(wǎng)絡(luò)緩存之前熊户,需要用到的第三方庫(kù)有FMDB
,AFNetworking
,這兩個(gè)大家都很熟悉了吭服,就不說(shuō)了嚷堡。
重點(diǎn)是這個(gè)基于FMDB
封裝的key-value
存儲(chǔ)框架 YTKKeyValueStore,該庫(kù)是出自猿題庫(kù)公司開(kāi)源的輕量級(jí)存儲(chǔ)框架,源碼也就400多行艇棕。但是這種存儲(chǔ)方式足以滿足各大中小型APP了蝌戒。關(guān)于怎么使用,并無(wú)學(xué)習(xí)成本沼琉”惫叮看該庫(kù)的GitHub鏈接,1分鐘就能上手打瘪!
實(shí)現(xiàn)邏輯
學(xué)會(huì)了怎么使用友鼻,接下來(lái)結(jié)合代碼,簡(jiǎn)單說(shuō)下實(shí)現(xiàn)思路:
1.創(chuàng)建類繼承自AFHTTPSessionManager
,然后根據(jù)load
方法的妙用闺骚,自動(dòng)監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài)彩扔,獲取網(wǎng)絡(luò)狀態(tài)后對(duì)其處理,有網(wǎng)加載僻爽,沒(méi)網(wǎng)本地取出虫碉。
+ (void)load
{
//設(shè)置網(wǎng)絡(luò)請(qǐng)求的基本屬性
JMHttpRequestMethod *httpMethod;
httpMethod.requestSerializer = [AFJSONRequestSerializer serializer];
//設(shè)置請(qǐng)求的超時(shí)時(shí)間
httpMethod.requestSerializer.timeoutInterval = 20.f;
//設(shè)置服務(wù)器返回結(jié)果的類型:JSON (AFJSONResponseSerializer,AFHTTPResponseSerializer)
httpMethod.responseSerializer = [AFJSONResponseSerializer serializer];
httpMethod.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:
@"application/json",
@"text/html",
@"text/json",
@"text/plain",
@"text/javascript",
@"text/xml", @"image/*", nil];
//創(chuàng)建數(shù)據(jù)庫(kù)和表
_store = [[YTKKeyValueStore alloc] initDBWithName:httpCache];
[_store createTableWithName:httpCache];
//開(kāi)始監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài)
[self startMonitoringNetworkStatus];
}
/**
監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài)
*/
+ (void)startMonitoringNetworkStatus
{
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status)
{
case AFNetworkReachabilityStatusUnknown:
//設(shè)置全局BOOL變量 獲取網(wǎng)絡(luò)狀態(tài)
_isHasNetWork = NO;
_status = JMNetworkStatusUnknown;
break;
case AFNetworkReachabilityStatusNotReachable:
_isHasNetWork = NO;
_status = JMNetworkStatusNotNetWork;
NSLog(@"沒(méi)有網(wǎng)的狀態(tài)");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
_isHasNetWork = YES;
_status = JMNetworkStatusReachableViaWWAN;
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
_isHasNetWork = YES;
_status = JMNetworkStatusReachableViaWiFi;
NSLog(@"現(xiàn)在是有網(wǎng)狀態(tài)");
break;
}
}];
[manager startMonitoring];
}
/**
獲取當(dāng)前的網(wǎng)絡(luò)狀態(tài)
@return YES 有網(wǎng) NO 沒(méi)有聯(lián)網(wǎng)
*/
+(BOOL)getCurrentNetWorkStatus
{
return _isHasNetWork;
}
2.GET
和 POST
請(qǐng)求接口添加布爾屬性,供選擇是否選擇對(duì)該URL
緩存
/**
GET 請(qǐng)求 帶有進(jìn)度回調(diào)的 API
@param url 請(qǐng)求的url
@param refreshCache 是否對(duì)該頁(yè)面進(jìn)行緩存
@param params 請(qǐng)求數(shù)據(jù)向服務(wù)器傳的參數(shù)
@param progress 請(qǐng)求進(jìn)度回調(diào)
@param success 請(qǐng)求成功回調(diào)
@param fail 請(qǐng)求失敗回調(diào)
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail;
/**
POST 請(qǐng)求 帶有進(jìn)度回調(diào)的 API
@param url 請(qǐng)求的url
@param refreshCache 是否對(duì)該頁(yè)面進(jìn)行緩存
@param params 請(qǐng)求數(shù)據(jù)向服務(wù)器傳的參數(shù)
@param progress 請(qǐng)求進(jìn)度回調(diào)
@param success 請(qǐng)求成功回調(diào)
@param fail 請(qǐng)求失敗回調(diào)
@return self
*/
+ (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; ```
3.每次調(diào)用請(qǐng)求接口胸梆,先在本地?cái)?shù)據(jù)庫(kù)讀取緩存敦捧,如果本地沒(méi)有再進(jìn)行請(qǐng)求,請(qǐng)求完之后對(duì)其緩存乳绕。這里以```GET ```請(qǐng)求為例绞惦, ```POST ```請(qǐng)求也一樣的邏輯。
/**
GET 請(qǐng)求 帶有進(jìn)度回調(diào)的 API
@param url 請(qǐng)求的url
@param refreshCache 是否對(duì)該頁(yè)面進(jìn)行緩存
@param params 請(qǐng)求數(shù)據(jù)向服務(wù)器傳的參數(shù)
@param progress 請(qǐng)求進(jìn)度回調(diào)
@param success 請(qǐng)求成功回調(diào)
@param fail 請(qǐng)求失敗回調(diào)
@return self
*/
- (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
JMHttpRequestMethod *request = nil;
//先獲取當(dāng)前的網(wǎng)絡(luò)狀態(tài)有網(wǎng)的話洋措,進(jìn)行請(qǐng)求
if ([JMHttpRequestMethod getCurrentNetWorkStatus]) {
//判斷是否對(duì)該url進(jìn)行緩存
if (!refreshCache) {
[self requestNotCacheWithHttpMethod:0 url:url params:params progress:progress success:success fail:fail];
}else {
//如果進(jìn)行緩存,先從本地取出緩存的數(shù)據(jù)
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
//如果本地有數(shù)據(jù)杰刽,直接回調(diào)菠发,否則進(jìn)行網(wǎng)絡(luò)請(qǐng)求
if (dict) {
success(dict);
}else {
[[self manager] GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 請(qǐng)求到數(shù)據(jù)后就進(jìn)行存儲(chǔ)王滤,然后回調(diào)請(qǐng)求成功的數(shù)據(jù)
[_store putObject:responseObject withId:url intoTable:httpCache];
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
NSLog(@"error = %@",error.description);
}];
}
}
} else {
//當(dāng)前沒(méi)有網(wǎng)絡(luò),然后從本地?cái)?shù)據(jù)庫(kù)中取出數(shù)據(jù)
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
//本地有數(shù)據(jù)的話滓鸠,進(jìn)行回調(diào)雁乡,否則取出失敗打印信息
if (dict) {
success(dict);
}else {
NSLog(@"當(dāng)前為無(wú)網(wǎng)絡(luò)狀態(tài),本地也沒(méi)有緩存數(shù)據(jù)");
}
}
return request;
/**
不進(jìn)行緩存時(shí)糜俗,進(jìn)行網(wǎng)絡(luò)請(qǐng)求的方法
@param httpMethod 0 : 代表GET請(qǐng)求 1:代表POST請(qǐng)求
@param url 請(qǐng)求的url
@param params 請(qǐng)求參數(shù)
@param progress 請(qǐng)求進(jìn)度回調(diào)
@param success 請(qǐng)求成功回調(diào)
@param fail 請(qǐng)求失敗回調(diào)
*/
- (void)requestNotCacheWithHttpMethod:(NSInteger)httpMethod
url:(NSString *)url
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
if (httpMethod == 0) {
[[self manager]GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}else {
[[self manager ]POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}
}
4.提供清除緩存和獲取文件大小接口
/**
獲取網(wǎng)絡(luò)緩存 文件大小
@return size 直接返回字符串踱稍,單位M。保留兩位小數(shù)悠抹。舉例 1.12M
*/
- (NSString )fileSizeWithDBPath
{
NSFileManager manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache]]){
unsigned long long fileSize = [[manager attributesOfItemAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache] error:nil] fileSize];
NSString *size = [NSString stringWithFormat:@"%.2fM",fileSize/1024.0/1024.0];
return size;
}else {
return @"0M";
}
return 0;
}
/**
清除所有網(wǎng)絡(luò)緩存
*/
- (void)cleanNetWorkRefreshCache
{
NSError *error;
BOOL isSuccess = [[NSFileManager defaultManager]removeItemAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache] error:&error];
if (isSuccess) {
NSLog(@"clean cache file is success");
}else {
if ([PATH_OF_NetWork stringByAppendingPathComponent:httpCache]) {
NSLog(@"error:%@",error.description);
}else {
NSLog(@"error: cache file is not exist");
}
}
}
本文大致實(shí)現(xiàn)邏輯就是這樣珠月,如果哪些不對(duì)的地方,可以提問(wèn)哈楔敌。
好久沒(méi)更新了啤挎,今后經(jīng)常更新下,記錄下自己所遇到的坑卵凑,這樣才能進(jìn)步庆聘!
[詳情請(qǐng)見(jiàn)本文github鏈接](https://github.com/JimmyLession/JMRequestNetWorkCache)