iOS網(wǎng)絡請求緩沖優(yōu)化HttpCachesLoader

前言:目前的開發(fā)中,處理網(wǎng)絡請求的時候其實很多重復的請求,例如請求廣告詞語之類,每次打開app都會重復上一次的請求。
基于這個重復的考慮,想要優(yōu)化我們就要把已經(jīng)請求過的響應體做成持久化的存儲,下一次再有重復的請求就可以直接從緩沖里面取出而不必要再次進行網(wǎng)絡請求。

根據(jù)我們的設想,可分如下步驟:

  1. 已經(jīng)成功請求的響應,進行持久化存儲。

  2. 設置持久化存儲的緩沖有效期

  3. 再次進行相應的請求,可以根據(jù)不同情況是否再次發(fā)送網(wǎng)絡請求,如下:

    a.沒有持久化緩沖
       如果沒有緩沖過的,只能發(fā)起新的網(wǎng)絡請求,然后進行持久化緩存
    
    b.已經(jīng)持久化緩沖,緩沖已經(jīng)過期
       已經(jīng)過期的緩存,也只能發(fā)起新的網(wǎng)絡請求,如果網(wǎng)絡請求失敗,只能把過期的緩存返回
    
    c.已經(jīng)持久化緩沖,緩沖還在有效期內(nèi),但是要求強行更新請求的設置
       即時已經(jīng)有還在有效期內(nèi)的緩存,但是如果發(fā)起強行更新的命令,也要從新發(fā)送新的網(wǎng)絡請求,同時也要更新數(shù)據(jù)庫的緩存和時間有限期
    
    d.已經(jīng)持久化緩沖,緩沖還在有效期內(nèi),并且沒有強行更新請求的設置
       已經(jīng)有還在有效期內(nèi)的緩存,并且沒有強行更新請求,那么不必要在發(fā)送新的網(wǎng)絡請求,直接從數(shù)據(jù)庫緩存中返回響應體
    

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路徑創(chuàng)建一個數(shù)據(jù)庫,并且建立一個存放緩沖的表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  請求參數(shù)
     *  @param immediately  是否強制更新
     *  @param block 請求完成后的回調(diào),id類型存放請求返回的數(shù)據(jù)
     */
    -(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

實現(xiàn)文件,這里主要說實現(xiàn)思路(使用到FMDB 和 AFNetworking框架)

1.如果通過默認的類方法創(chuàng)建HttpCachesLoader對象,那么在默認的沙河路徑caches文件夾里面會創(chuàng)建一個數(shù)據(jù)庫文件,并且建立一個存放緩沖的表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(@"---默認數(shù)據(jù)庫存放地址---:%@",path);
              _queue = [FMDatabaseQueue databaseQueueWithPath:path];
              [self setup];
          }
          return self;
      }

2.使用相應的對象方法發(fā)送請求

-(void)GET:(NSString *)url  parameters:(NSDictionary *)dict immediately:(BOOL)immediately completion:(void(^)(id))block;

#舉例說明該對象方法的實現(xiàn)思路

//拼接url和參數(shù)  主要用來查詢數(shù)據(jù)庫的key
NSString *url_parameters = [NSString stringWithFormat:@"%@?%@",url,[self sortParameter:dict]];
//根據(jù)url_parameters查找是否已經(jīng)緩沖數(shù)據(jù)
NSString *sqlSelect = [NSString stringWithFormat:@"select * from HttpRequestCaches where request = '%@'",url_parameters];

//是否緩沖的標志
__block BOOL isCaches = NO;
//緩沖是否過期的標志
__block BOOL isCachesTimeOut = NO;

//重開數(shù)據(jù)庫連接
[self.queue restartDataBase];

//先到數(shù)據(jù)查詢是否有相應的記錄
[self.queue inDatabase:^(FMDatabase *db) {
    
    FMResultSet *result = [db executeQuery:sqlSelect];
    
    if ((isCaches =[result next])) {

        NSTimeInterval cachesInterval = [[NSDate localDate] timeIntervalSinceDate:[NSDate dateFromString:[result stringForColumn:@"time"]]];
            
        //計算緩沖是否已經(jīng)過期  
        isCachesTimeOut = cachesInterval > self.timeLimit;
        //緩沖還沒過期,先把結(jié)果從數(shù)據(jù)庫取出           
        if (!isCachesTimeOut) dataResult = [result dataForColumn:@"response"];

        [result close];
    }
}];

//數(shù)據(jù)操作
NSString *sqlUpdateOrInsert;

if (!isCaches) {
    //如果沒有緩沖過,執(zhí)行插入輸入庫操作
    sqlUpdateOrInsert = @"insert into HttpRequestCaches (time,response,url,request) values (?,?,?,?)";
    NSLog(@"沒有緩沖過,執(zhí)行插入輸入庫操作");
}else{
    //如果緩沖沒有超時,而且請求為immediately=NO的話,證明數(shù)據(jù)庫中的緩沖還符合要求,取出結(jié)果返回。結(jié)束請求
    if (!immediately && !isCachesTimeOut) {
        if (block) {
            NSLog(@"緩沖沒有超時,而且請求為immediately=NO的話,證明數(shù)據(jù)庫中的緩沖還符合要求,取出結(jié)果返回。結(jié)束請求");
            block(dataResult);
        }
        return;
    }
    //如果緩沖過但是已經(jīng)超時,或者請求為immediately=YES的話,執(zhí)行跟新數(shù)據(jù)庫操作
    sqlUpdateOrInsert = @"update HttpRequestCaches set time = ? ,response = ? ,url = ? where request = ?";
    NSLog(@"緩沖過但是已經(jīng)超時,或者請求為immediately=YES的話,執(zhí)行跟新數(shù)據(jù)庫操作");
}

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;
        //把最終結(jié)果通過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];
            }
        }];
        //把最終結(jié)果通過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;
        //把最終結(jié)果通過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];
            }
        //把最終結(jié)果通過block傳遞出去
            if (block) {
                block(dataResult);
            }
        }];
    }];
    
}

項目git地址:https://github.com/kinglchristina/HttpCachesLoader.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • AFHTTPRequestOperationManager 網(wǎng)絡傳輸協(xié)議UDP、TCP、Http、Socket、X...
    Carden閱讀 4,404評論 0 12
  • iOS網(wǎng)絡架構(gòu)討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,259評論 1 23
  • API定義規(guī)范 本規(guī)范設計基于如下使用場景: 請求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請求頻率非常高,建議使用雙通...
    有涯逐無涯閱讀 2,609評論 0 6
  • feisky云計算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 3,926評論 0 5
  • 希 望 希望是什么?希望是潘多拉盒子底深藏著的唯一美好的東西,但是希望被永遠的關在了盒子里。 希望是一棵...
    星明海闊閱讀 879評論 0 9