iOS開(kāi)發(fā)-網(wǎng)絡(luò)緩存封裝

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.GETPOST請(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市勺卢,隨后出現(xiàn)的幾起案子伙判,更是在濱河造成了極大的恐慌,老刑警劉巖黑忱,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宴抚,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杨何,警方通過(guò)查閱死者的電腦和手機(jī)酱塔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)危虱,“玉大人羊娃,你說(shuō)我怎么就攤上這事“u危” “怎么了蕊玷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)弥雹。 經(jīng)常有香客問(wèn)我垃帅,道長(zhǎng),這世上最難降的妖魔是什么剪勿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任贸诚,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘酱固。我一直安慰自己械念,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布运悲。 她就那樣靜靜地躺著龄减,像睡著了一般。 火紅的嫁衣襯著肌膚如雪班眯。 梳的紋絲不亂的頭發(fā)上希停,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音署隘,去河邊找鬼宠能。 笑死,一個(gè)胖子當(dāng)著我的面吹牛定踱,可吹牛的內(nèi)容都是我干的棍潘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼崖媚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼亦歉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起畅哑,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肴楷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后荠呐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赛蔫,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年泥张,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呵恢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡媚创,死狀恐怖渗钉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞钙,我是刑警寧澤鳄橘,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站芒炼,受9級(jí)特大地震影響瘫怜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜本刽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一鲸湃、第九天 我趴在偏房一處隱蔽的房頂上張望赠涮。 院中可真熱鬧,春花似錦唤锉、人聲如沸世囊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蝙寨,卻和暖如春晒衩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背墙歪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工听系, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虹菲。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓靠胜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親毕源。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浪漠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)霎褐,斷路器址愿,智...
    卡卡羅2017閱讀 134,667評(píng)論 18 139
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件冻璃、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,107評(píng)論 4 62
  • 2017 年 2 月 14 日省艳,西方情人節(jié)娘纷,作為 30+ 的大齡女青年,還沒(méi)有可結(jié)婚的對(duì)象跋炕,頓覺(jué)需要把這件事情提上...
    朱家曉曉閱讀 497評(píng)論 2 4
  • 生命的重啟 : 咸 __ 漢徘 作者 : 何 強(qiáng) " 沒(méi)有一聲巨響赖晶,世界不會(huì)理你的 " 不 染 塵 俗 誰(shuí) 紅 透...
    天邊的凝紅閱讀 254評(píng)論 0 0
  • 這篇文章是我?guī)讉€(gè)月前看到的棉圈,今日讀來(lái)涩堤,又有了新的啟發(fā)。 張泉靈曾經(jīng)是央視著名的主持人分瘾,在她主持事業(yè)的高峰期辭職胎围,進(jìn)...
    非寧?kù)o無(wú)以致遠(yuǎn)_淵寧閱讀 177評(píng)論 0 1