美文网首页
封装AFN3.0网络请求框架,使用FMDB缓存并可带有时效性缓存

封装AFN3.0网络请求框架,使用FMDB缓存并可带有时效性缓存

作者: Masazumi柒 | 来源:发表于2017-07-26 18:24 被阅读0次

    现在项目处于维护阶段,以前的一部分代码随着用户量的增加,不断暴露出问题,特别是网络请求--回调处理方面的修改比较多。每次修改对应的接口都要跳到对应控制器啊,或者是搜一发提示语,其他网络请求操作也是分散在各个控制器内,乱七八糟。好烦的。趁着有点空闲,研究了下别人的封装,改进了项目中使用的封装,并做成了SDK发布在cocoaPods上,欢迎使用.
    pod 'TYNetworkTool'即可使用。

    项目原先的AFN封装

    也就是很简单的按照AFN封装了一下,.m
    //
    //  PPRHttpManager.m
    //  PaopaoRunning
    //
    //  Created by 王天永 on 16/5/22.
    
    //
    
    #import "PPRHttpManager.h"
    #import <AFNetworking.h>
    #import "NSString+Paths.h"
    #import "NSString+UUID.h"
    
    @implementation PPRHttpManager
    + (NSURLSessionDataTask *)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
        
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        if (IOS11) {    //适配IOS11增加的这句
            manager.securityPolicy.allowInvalidCertificates = YES;
        }
        manager.requestSerializer.timeoutInterval = 15;
    //    [manager.requestSerializer setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        NSURLSessionDataTask *task = [manager GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            if (success){
                NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
                PRLog(@"responseString---%@",responseString);
                NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
                success(dict);
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            if (failure){
                failure(error);
            }
        }];
        return task;
    }
    
    + (NSURLSessionDataTask *)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    //    manager.securityPolicy.allowInvalidCertificates = YES;
        manager.requestSerializer.timeoutInterval = 15;
        NSURLSessionDataTask *task = [manager POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            if (success){
                NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
                PRLog(@"responseString---%@",responseString);
                
                NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
    
                success(dict);
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            if (failure){
    
                failure(error);
            }
        }];
        return task;
    
    }
    
    + (NSURLSessionDownloadTask *)downloadFile:(NSString *)url progress:(void (^)(CGFloat progress))progress success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
    
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
        
        NSURL *URL = [NSURL URLWithString:url];
        NSURLRequest *request = [NSURLRequest requestWithURL:URL];
        NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
    //    NSLog(@"progress is %f", downloadProgress.fractionCompleted);
            progress(downloadProgress.fractionCompleted);
        } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
            NSString *caches = [NSString cachesPath];
            NSString *videovideoCaches = [caches stringByAppendingPathComponent:@"videoCaches"];
            
            NSURL *videoFileUrl = [NSURL fileURLWithPath:videovideoCaches];
            return [videoFileUrl URLByAppendingPathComponent:[response suggestedFilename] ];
        } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
            if (!error){
                PRLog(@"%@",F(@"File downloaded to: %@", filePath));
                success(filePath);
                
                
            }else{
                PRLog(@"File download fail!");
                failure(error);
            }
        }];
        [downloadTask resume];
        return downloadTask;
        
    }
    
    @end
    

    着手封装新的低耦合的AFN网络封装

    封装的主要方向

    1. 本地缓存优化,请求可根据需求(缓存的时效性等)决定是否直接使用本地缓存,或者请求到新数据后刷新本地。(当前已完成)
    2. 增加短时缓存,避免相同的请求重复调用,浪费用户流量。(当前已完成)
    3. 封装请求失败的重试机制。
    4. 将成功失败提示的逻辑封装。
    5. 其他比如网络状况监控,请求一键撤销等。(当前已完成)
      感谢巨巨文章提供思路和参考
      猿题库YTKNetwork的GitHub
      iOS 工作中封装通用性网络请求框架——鸿雁长飞光不度
      AFNetworking3.x与YYCache的二次封装,和FMDB说拜拜——jkpang

    封装的结构考虑

    1. 考虑以后换网络框架的可能。(网络单独封装
    2. 考虑以后换回调处理及提示的可能。网络请求和回调处理要低耦合,能够单独抽离。(回调和显示的单独封装
    3. 考虑以后换缓存框架的可能。(缓存单独封装

    封装的结构

    考虑到网络请求的通用性,而回调处理和提示等并不通用,所以回调的封装暂时不纳入SDK,但是仍然建议将网络请求的回调封装起来存于一处,相同回调调用相应的统一方法,以后需要改动改这一处就够了,如本人GitHub上的TYNetworkManage文件夹内所封装的方式。
    所以最终的SDK内的文件结构是这样的(暂时

    TYNetworkTool封装结构
    · 第一层 网络请求入口 TYNetworkTool,.h文件对外暴露,用户之间调用即可完成网络请求。.m调用TYCacheTool.h的方法来完成缓存的存储和读取;
    · 第二层 缓存的存取入口TYCacheTool,主要来完成缓存的业务逻辑
    · 第三层 FMDB的实际缓存操作,实际操控SQLite文件来进行存取操作。
    文件内容
    //
    //  TYNetworkTool.h
    //  TYNetworkHelper
    //
    //  Created by 王天永 on 2017/7/14.
    //  Copyright © 2017年 王天永. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import "TYCacheTool.h"
    
    typedef NS_ENUM(NSUInteger, TYNetworkStatusType) {
        /// 未知网络
        TYNetworkStatusUnknown,
        /// 无网络
        TYNetworkStatusNotReachable,
        /// 手机网络
        TYNetworkStatusReachableViaWWAN,
        /// WIFI网络
        TYNetworkStatusReachableViaWiFi
    };
    
    typedef NS_ENUM(NSUInteger, TYRequestSerializer) {
        /// 设置请求数据为JSON格式
        TYRequestSerializerJSON,
        /// 设置请求数据为二进制格式
        TYRequestSerializerHTTP,
    };
    
    typedef NS_ENUM(NSUInteger, TYResponseSerializer) {
        /// 设置响应数据为JSON格式
        TYResponseSerializerJSON,
        /// 设置响应数据为二进制格式
        TYResponseSerializerHTTP,
    };
    
    /// 请求成功的Block
    typedef void(^TYHttpRequestSuccess)(id responseObject);
    
    /// 请求失败的Block
    typedef void(^TYHttpRequestFailed)(NSError *error);
    
    /// 缓存的Block
    typedef void(^TYHttpRequestCache)(id responseCache);
    
    /// 上传或者下载的进度, Progress.completedUnitCount:当前大小 - Progress.totalUnitCount:总大小
    typedef void (^TYHttTYrogress)(NSProgress *progress);
    
    /// 网络状态的Block
    typedef void(^TYNetworkStatus)(TYNetworkStatusType status);
    
    @class AFHTTPSessionManager;
    @interface TYNetworkTool : NSObject
    
    /// 有网YES, 无网:NO
    + (BOOL)isNetwork;
    
    /// 手机网络:YES, 反之:NO
    + (BOOL)isWWANNetwork;
    
    /// WiFi网络:YES, 反之:NO
    + (BOOL)isWiFiNetwork;
    
    /// 取消所有HTTP请求
    + (void)cancelAllRequest;
    
    /// 实时获取网络状态,通过Block回调实时获取(此方法可多次调用)
    + (void)networkStatusWithBlock:(TYNetworkStatus)networkStatus;
    
    /// 取消指定URL的HTTP请求
    + (void)cancelRequestWithURL:(NSString *)URL;
    
    /// 开启日志打印 (Debug级别)
    + (void)openLog;
    
    /// 关闭日志打印,默认关闭
    + (void)closeLog;
    
    
    /**
     *  GET请求,无缓存
     *
     *  @param URL        请求地址
     *  @param parameters 请求参数
     *  @param success    请求成功的回调
     *  @param failure    请求失败的回调
     *
     *  @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)GET:(NSString *)URL
                            parameters:(id)parameters
                               success:(TYHttpRequestSuccess)success
                               failure:(TYHttpRequestFailed)failure;
    
    /**
     *  GET请求,自动缓存
     *
     *  @param URL           请求地址
     *  @param parameters    请求参数
     *  @param responseCache 缓存数据的回调
     *  @param success       请求成功的回调
     *  @param failure       请求失败的回调
     *
     *  @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)GET:(NSString *)URL
                            parameters:(id)parameters
                         responseCache:(TYHttpRequestCache)responseCache
                               success:(TYHttpRequestSuccess)success
                               failure:(TYHttpRequestFailed)failure;
    
    /**
     GET请求,带时效自动缓存
    
     @param URL 请求地址
     @param parameters 请求参数
     @param life 缓存时效
     @param responseCache 缓存数据的回调
     @param success 请求成功的回调
     @param failure 请求失败的回调
     @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)GET:(NSString *)URL
                            parameters:(id)parameters
                       userfulLifeUnit:(TYTimeUnit)timeUnit
                          userfullLife:(double)life
                         responseCache:(TYHttpRequestCache)responseCache
                               success:(TYHttpRequestSuccess)success
                               failure:(TYHttpRequestFailed)failure;
    /**
     *  POST请求,无缓存
     *
     *  @param URL        请求地址``
     *  @param parameters 请求参数
     *  @param success    请求成功的回调
     *  @param failure    请求失败的回调
     *
     *  @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)POST:(NSString *)URL
                             parameters:(id)parameters
                                success:(TYHttpRequestSuccess)success
                                failure:(TYHttpRequestFailed)failure;
    
    /**
     *  POST请求,自动缓存
     *
     *  @param URL           请求地址
     *  @param parameters    请求参数
     *  @param responseCache 缓存数据的回调
     *  @param success       请求成功的回调
     *  @param failure       请求失败的回调
     *
     *  @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)POST:(NSString *)URL
                             parameters:(id)parameters
                          responseCache:(TYHttpRequestCache)responseCache
                                success:(TYHttpRequestSuccess)success
                                failure:(TYHttpRequestFailed)failure;
    
    /**
     GET请求,带时效自动缓存
     
     @param URL 请求地址
     @param parameters 请求参数
     @param life 缓存时效
     @param responseCache 缓存数据的回调
     @param success 请求成功的回调
     @param failure 请求失败的回调
     @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)POST:(NSString *)URL
                            parameters:(id)parameters
                       userfulLifeUnit:(TYTimeUnit)timeUnit
                          userfullLife:(double)life
                         responseCache:(TYHttpRequestCache)responseCache
                               success:(TYHttpRequestSuccess)success
                               failure:(TYHttpRequestFailed)failure;
    
    /**
     *  上传文件
     *
     *  @param URL        请求地址
     *  @param parameters 请求参数
     *  @param name       文件对应服务器上的字段
     *  @param filePath   文件本地的沙盒路径
     *  @param progress   上传进度信息
     *  @param success    请求成功的回调
     *  @param failure    请求失败的回调
     *
     *  @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
                                          parameters:(id)parameters
                                                name:(NSString *)name
                                            filePath:(NSString *)filePath
                                            progress:(TYHttTYrogress)progress
                                             success:(TYHttpRequestSuccess)success
                                             failure:(TYHttpRequestFailed)failure;
    
    /**
     *  上传单/多张图片
     *
     *  @param URL        请求地址
     *  @param parameters 请求参数
     *  @param name       图片对应服务器上的字段
     *  @param images     图片数组
     *  @param fileNames  图片文件名数组, 可以为nil, 数组内的文件名默认为当前日期时间"yyyyMMddHHmmss"
     *  @param imageScale 图片文件压缩比 范围 (0.f ~ 1.f)
     *  @param imageType  图片文件的类型,例:png、jpg(默认类型)....
     *  @param progress   上传进度信息
     *  @param success    请求成功的回调
     *  @param failure    请求失败的回调
     *
     *  @return 返回的对象可取消请求,调用cancel方法
     */
    + (__kindof NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
                                            parameters:(id)parameters
                                                  name:(NSString *)name
                                                images:(NSArray<UIImage *> *)images
                                             fileNames:(NSArray<NSString *> *)fileNames
                                            imageScale:(CGFloat)imageScale
                                             imageType:(NSString *)imageType
                                              progress:(TYHttTYrogress)progress
                                               success:(TYHttpRequestSuccess)success
                                               failure:(TYHttpRequestFailed)failure;
    
    /**
     *  下载文件
     *
     *  @param URL      请求地址
     *  @param fileDir  文件存储目录(默认存储目录为Download)
     *  @param progress 文件下载的进度信息
     *  @param success  下载成功的回调(回调参数filePath:文件的路径)
     *  @param failure  下载失败的回调
     *
     *  @return 返回NSURLSessionDownloadTask实例,可用于暂停继续,暂停调用suspend方法,开始下载调用resume方法
     */
    + (__kindof NSURLSessionTask *)downloadWithURL:(NSString *)URL
                                           fileDir:(NSString *)fileDir
                                          progress:(TYHttTYrogress)progress
                                           success:(void(^)(NSString *filePath))success
                                           failure:(TYHttpRequestFailed)failure;
    
    
    #pragma mark - 设置AFHTTPSessionManager相关属性
    #pragma mark 注意: 因为全局只有一个AFHTTPSessionManager实例,所以以下设置方式全局生效
    /**
     在开发中,如果以下的设置方式不满足项目的需求,就调用此方法获取AFHTTPSessionManager实例进行自定义设置
     (注意: 调用此方法时在要导入AFNetworking.h头文件,否则可能会报找不到AFHTTPSessionManager的❌)
     @param sessionManager AFHTTPSessionManager的实例
     */
    + (void)setAFHTTPSessionManagerProperty:(void(^)(AFHTTPSessionManager *sessionManager))sessionManager;
    
    /**
     *  设置网络请求参数的格式:默认为二进制格式
     *
     *  @param requestSerializer TYRequestSerializerJSON(JSON格式),TYRequestSerializerHTTP(二进制格式),
     */
    + (void)setRequestSerializer:(TYRequestSerializer)requestSerializer;
    
    /**
     *  设置服务器响应数据格式:默认为JSON格式
     *
     *  @param responseSerializer TYResponseSerializerJSON(JSON格式),TYResponseSerializerHTTP(二进制格式)
     */
    + (void)setResponseSerializer:(TYResponseSerializer)responseSerializer;
    
    /**
     *  设置请求超时时间:默认为30S
     *
     *  @param time 时长
     */
    + (void)setRequestTimeoutInterval:(NSTimeInterval)time;
    
    /// 设置请求头
    + (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
    
    /**
     *  是否打开网络状态转圈菊花:默认打开
     *
     *  @param open YES(打开), NO(关闭)
     */
    + (void)openNetworkActivityIndicator:(BOOL)open;
    
    /**
     配置自建证书的Https请求, 参考链接: http://blog.csdn.net/syg90178aw/article/details/52839103
     
     @param cerPath 自建Https证书的路径
     @param validatesDomainName 是否需要验证域名,默认为YES. 如果证书的域名与请求的域名不一致,需设置为NO; 即服务器使用其他可信任机构颁发
     的证书,也可以建立连接,这个非常危险, 建议打开.validatesDomainName=NO, 主要用于这种情况:客户端请求的是子域名, 而证书上的是另外
     一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com, 那么mail.google.com是无法验证通过的.
     */
    + (void)setSecurityPolicyWithCerPath:(NSString *)cerPath validatesDomainName:(BOOL)validatesDomainName;
    
    + (NSString *)jsonToString:(id)data;
    @end
    
    
    //
    //  TYCacheTool.h
    //  TYNetworkTool
    //
    //  Created by 王天永 on 2017/7/19.
    //  Copyright © 2017年 王天永. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    typedef NS_ENUM (NSUInteger,TYTimeUnit) {
        TYNone,
        TYSecond,
        TYMinute,
        TYHour,
        TYDay,
    };
    
    @interface TYCacheTool : NSObject
    
    /**
     无失效日期缓存
    
     @param httpData 缓存数据
     @param url 请求url
     @param parameters 请求参数
     */
    + (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters;
    /**
     带时效的缓存
    
     @param httpData 缓存数据
     @param url 请求url
     @param parameters 请求参数
     @param timeUnit 时间单位,TYNone则无时效
     @param life 时效数值,具体单位与timeUnit有关,最小单位 秒
     */
    + (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters userfulLifeUnit:(TYTimeUnit)timeUnit life:(double)life;
    /**
     获取缓存数据
     
     @param url 请求url
     @param parameter 请求参数
     @return 缓存数据
     */
    + (id)httpCacheWithURL:(NSString *)url parameters:(NSDictionary *)parameter;
    
    @end
    
    
    //
    //  TYFMDBTool.h
    //  TYNetworkTool
    //
    //  Created by 王天永 on 2017/7/20.
    //  Copyright © 2017年 王天永. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    @class TYCacheTool;
    
    
    @interface TYFMDBTool : NSObject
    
    
    /**
     创建tool实例,包含一个dmdb实例对象
    
     @param name 对应账号的唯一标识符
     @return 返回fmdbtool实例
     */
    + (instancetype)fmdbWithName:(NSString *)name;
    
    /**
     创建表单
    
     @param tableName 表单名
     @return FMDB实例
     */
    - (instancetype)createTableWithName:(NSString *)tableName;
    
    
    /**
     储存缓存请求来的回调数据(无时效)
     
     @param httpData 回调数据
     @param cacheKey 存储的关键字
     */
    - (void)setHttpCache:(id)httpData forCacheKey:(NSString *)cacheKey;
    
    /**
     带时效的缓存
     
     @param httpData 缓存数据
     @param cacheKey 存储的关键字
     @param life 时效数值,具体单位与timeUnit有关
     */
    - (void)setHttpCache:(id)httpData userfulLife:(double)life forCacheKey:(NSString *)cacheKey;
    
    
    /**
     获取本地缓存
    
     @param cacheKey 存储的关键字
     @return 缓存本地的网络请求
     */
    - (id)httpCacheForCacheKey:(NSString *)cacheKey;
    @end
    
    

    感觉带时效的缓存还有改进的余地,后续再继续改进吧,以新增方法的方式。(当前的方式适合这样的需求:如果是有效的缓存,先读取了显示在界面上,等数据获取到了再显示获取到的数据。但是这样就没有达到节省流量的目的了,需要再SDK内增加先取缓存,无有效缓存再发送网络请求的方法
    本人的这个SDK已经发布在cocoapods上,pod 'TYNetworkTool'即可使用。

    相关文章

      网友评论

          本文标题:封装AFN3.0网络请求框架,使用FMDB缓存并可带有时效性缓存

          本文链接:https://www.haomeiwen.com/subject/nolokxtx.html