前言:目前的开发中,处理网络请求的时候其实很多重复的请求,例如请求广告词语之类,每次打开app都会重复上一次的请求。
基于这个重复的考虑,想要优化我们就要把已经请求过的响应体做成持久化的存储,下一次再有重复的请求就可以直接从缓冲里面取出而不必要再次进行网络请求。
根据我们的设想,可分如下步骤:
-
已经成功请求的响应,进行持久化存储。
-
设置持久化存储的缓冲有效期
-
再次进行相应的请求,可以根据不同情况是否再次发送网络请求,如下:
a.没有持久化缓冲
如果没有缓冲过的,只能发起新的网络请求,然后进行持久化缓存b.已经持久化缓冲,缓冲已经过期 已经过期的缓存,也只能发起新的网络请求,如果网络请求失败,只能把过期的缓存返回 c.已经持久化缓冲,缓冲还在有效期内,但是要求强行更新请求的设置 即时已经有还在有效期内的缓存,但是如果发起强行更新的命令,也要从新发送新的网络请求,同时也要更新数据库的缓存和时间有限期 d.已经持久化缓冲,缓冲还在有效期内,并且没有强行更新请求的设置 已经有还在有效期内的缓存,并且没有强行更新请求,那么不必要在发送新的网络请求,直接从数据库缓存中返回响应体
HttpCachesLoader主要逻辑代码如下:
头文件
@class FMDatabaseQueue;
#import <Foundation/Foundation.h>
//定义缓冲有效期
#define MINUTE 60
#define HOUR 60*60
#define DAY HOUR*24
#define MONTH DAY*30
#define YEAR MONTH*12
//定义请求方式
typedef NS_ENUM(NSInteger , HttpRequestMethod) {
HttpRequestMethodGet ,
HttpRequestMethodPost
};
@interface HttpCachesLoader : NSObject
/** 缓冲有效期 单位:秒 默认1min*/
@property (nonatomic,assign)double timeLimit;
/**
* 默认的类方法,会在沙河的caches路径创建一个数据库,并且建立一个存放缓冲的表HttpRequestCaches
*
* @return 返回一个HttpCachesLoader对象
*/
+(instancetype)loaderDefault;
+(instancetype)loaderWithPath:(NSString *)path;
+(instancetype)loaderWithFMDatabaseQueue:(FMDatabaseQueue *)queue;
-(instancetype)initWithPath:(NSString *)path;
-(instancetype)initWithFMDatabaseQueue:(FMDatabaseQueue *)queue;
/**
* 通过GET 或者 POST请求方式
*
* @param url 请求地址
* @param dict 请求参数
* @param immediately 是否强制更新
* @param block 请求完成后的回调,id类型存放请求返回的数据
*/
-(void)GET:(NSString *)url parameters:(NSDictionary *)dict completion:(void(^)(id))block;
-(void)GET:(NSString *)url parameters:(NSDictionary *)dict immediately:(BOOL)immediately completion:(void(^)(id))block;
-(void)POST:(NSString *)url parameters:(NSDictionary *)dict completion:(void(^)(id))block;
-(void)POST:(NSString *)url parameters:(NSDictionary *)dict immediately:(BOOL)immediately completion:(void(^)(id))block;
@end
实现文件,这里主要说实现思路(使用到FMDB 和 AFNetworking框架)
1.如果通过默认的类方法创建HttpCachesLoader对象,那么在默认的沙河路径caches文件夹里面会创建一个数据库文件,并且建立一个存放缓冲的表HttpRequestCaches
HttpCachesLoader *loader = [HttpCachesLoader loaderDefault];
+(instancetype)loaderDefault{
HttpCachesLoader *loader = [[self alloc]init];
return loader;
}
-(instancetype)init{
if (self = [super init]) {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"defaultHttpCaches.db"];
NSLog(@"---默认数据库存放地址---:%@",path);
_queue = [FMDatabaseQueue databaseQueueWithPath:path];
[self setup];
}
return self;
}
2.使用相应的对象方法发送请求
-(void)GET:(NSString *)url parameters:(NSDictionary *)dict immediately:(BOOL)immediately completion:(void(^)(id))block;
#举例说明该对象方法的实现思路
//拼接url和参数 主要用来查询数据库的key
NSString *url_parameters = [NSString stringWithFormat:@"%@?%@",url,[self sortParameter:dict]];
//根据url_parameters查找是否已经缓冲数据
NSString *sqlSelect = [NSString stringWithFormat:@"select * from HttpRequestCaches where request = '%@'",url_parameters];
//是否缓冲的标志
__block BOOL isCaches = NO;
//缓冲是否过期的标志
__block BOOL isCachesTimeOut = NO;
//重开数据库连接
[self.queue restartDataBase];
//先到数据查询是否有相应的记录
[self.queue inDatabase:^(FMDatabase *db) {
FMResultSet *result = [db executeQuery:sqlSelect];
if ((isCaches =[result next])) {
NSTimeInterval cachesInterval = [[NSDate localDate] timeIntervalSinceDate:[NSDate dateFromString:[result stringForColumn:@"time"]]];
//计算缓冲是否已经过期
isCachesTimeOut = cachesInterval > self.timeLimit;
//缓冲还没过期,先把结果从数据库取出
if (!isCachesTimeOut) dataResult = [result dataForColumn:@"response"];
[result close];
}
}];
//数据操作
NSString *sqlUpdateOrInsert;
if (!isCaches) {
//如果没有缓冲过,执行插入输入库操作
sqlUpdateOrInsert = @"insert into HttpRequestCaches (time,response,url,request) values (?,?,?,?)";
NSLog(@"没有缓冲过,执行插入输入库操作");
}else{
//如果缓冲没有超时,而且请求为immediately=NO的话,证明数据库中的缓冲还符合要求,取出结果返回。结束请求
if (!immediately && !isCachesTimeOut) {
if (block) {
NSLog(@"缓冲没有超时,而且请求为immediately=NO的话,证明数据库中的缓冲还符合要求,取出结果返回。结束请求");
block(dataResult);
}
return;
}
//如果缓冲过但是已经超时,或者请求为immediately=YES的话,执行跟新数据库操作
sqlUpdateOrInsert = @"update HttpRequestCaches set time = ? ,response = ? ,url = ? where request = ?";
NSLog(@"缓冲过但是已经超时,或者请求为immediately=YES的话,执行跟新数据库操作");
}
if (method == HttpRequestMethodGet) {//GET请求
[self.mgr GET:url parameters:dict progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"---请求成功---%@",task.currentRequest.URL);
NSData *data = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil];
[self.queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:sqlUpdateOrInsert,[NSDate stringFromLocalDate],data,url,url_parameters];
}];
dataResult = data;
//把最终结果通过block传递出去
if (block) {
block(dataResult);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"---请求失败---%@",error);
[self.queue inDatabase:^(FMDatabase *db) {
FMResultSet *timeoutResult = [db executeQuery:sqlSelect];
if ([timeoutResult next]) {
dataResult = [timeoutResult dataForColumn:@"response"];
[timeoutResult close];
}
}];
//把最终结果通过block传递出去
if (block) {
block(dataResult);
}
}];
}else if(method == HttpRequestMethodPost){//POST请求
[self.mgr POST:url parameters:dict progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"---请求成功---%@",task.currentRequest.URL);
NSData *data = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil];
[self.queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:sqlUpdateOrInsert,[NSDate stringFromLocalDate],data,url,url_parameters];
}];
dataResult = data;
//把最终结果通过block传递出去
if (block) {
block(dataResult);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"---请求失败---%@",error);
[self.queue inDatabase:^(FMDatabase *db) {
FMResultSet *timeoutResult = [db executeQuery:sqlSelect];
if ([timeoutResult next]) {
dataResult = [timeoutResult dataForColumn:@"response"];
[timeoutResult close];
}
//把最终结果通过block传递出去
if (block) {
block(dataResult);
}
}];
}];
}
项目git地址:https://github.com/kinglchristina/HttpCachesLoader.git
网友评论