美文网首页
iOS 网络请求模块封装 - OC

iOS 网络请求模块封装 - OC

作者: arKenLee | 来源:发表于2016-07-18 23:32 被阅读0次

    Swift的Alamofire使用久了之后,回头写OC的项目使用 -request:success:failure: 这种形式的网络请求却不太习惯了……于是动手封装一个基于AFNetworking3.1框架Alamofire风格的网络请求模块

    代码github地址:ARKHTTPModule

    基础功能

    • 请求的创建
    • 获取请求的状态过程
    • 请求的操作
    • 响应

    这些基础功能的类为ARKRequest

    1、请求的创建

    通过请求地址、请求参数等创建一个网络请求实例

    // 常用的HTTP请求,默认GET请求,请求超时时长15秒
    + (instancetype)requestWithURLString:(NSString *)URLStr
                              parameters:(nullable NSDictionary *)params;
    
    + (instancetype)requestWithMethod:(ARKRequestMethod)method
                           serializer:(ARKRequestSerializerType)serializer
                            URLString:(NSString *)URLStr
                           parameters:(nullable NSDictionary *)params
                       requestTimeout:(NSTimeInterval)timeout;
    
    // 上传
    + (instancetype)uploadWithType:(ARKSessionType)type
                         URLString:(NSString *)URLStr
                        parameters:(nullable NSDictionary *)params
                          fromData:(nullable NSData *)bodyData;
    
    // 下载
    + (instancetype)downloadWithType:(ARKSessionType)type
                           URLString:(NSString *)URLStr
                          parameters:(nullable NSDictionary *)params
                         destination:(nonnull DownloadDidFinishTargetBlock)destination;
    
    // etc...
    

    2、获取请求的状态过程

    通过下列方法可以获取请求的进度、即将执行、即将暂停、即将取消、后台请求完毕状态

    // 请求进度
    - (ARKRequest *(^)(_Nullable ProgressBlock))requestProgressBlock;
    
    // 请求即将执行
    - (ARKRequest *(^)(_Nullable RequestStateBlock))requestWillResume;
    
    // 请求即将暂停
    - (ARKRequest *(^)(_Nullable RequestStateBlock))requestWillSuspend;
    
    // 请求即将取消
    - (ARKRequest *(^)(_Nullable RequestStateBlock))requestWillCancel;
    
    // 后台请求完毕闭包
    - (ARKRequest *(^)(_Nullable SessionDidFinishEventsForBackgroundURLSessionBlock))didFinishEventsForBackgroundURLSession;
    

    3、请求的操作

    对请求进行发起、暂停、取消等操作

    // 发送请求
    - (ARKRequest *(^)(void))resume;
    
    // 暂停请求
    - (ARKRequest *(^)(void))suspend;
    
    // 取消请求
    - (ARKRequest *(^)(void))cancel;
    

    4、响应

    这里参考了Alamofire的请求完毕回调形式
    ARKRequest类中有一个NSOperationQueue串行队列,该队列在创建的时候处于暂停状态
    用户在使用Response添加代码时,将Block代码放入这个队列中
    当有响应时,顺序执行队列中的Block

    // 返回NSData
    - (ARKRequest *(^)(ResponseDataBlock))responseDataOnMainThread;
    
    // 返回NSString
    - (ARKRequest *(^)(ResponseStringBlock))responseStringOnMainThread;
    
    // 返回JSON(NSArray或NSDictionary)
    - (ARKRequest *(^)(ResponseJSONBlock))responseJSONOnMainThread;
    
    // 返回JSON解析模型(目前默认是从JSON数据解析)
    // 只要自定义的数据模型遵守< ARKMappableObject >协议
    // 在返回JSON数据时,会根据传入的Class自动解析为对应的数据模型
    - (ARKRequest *(^)(ResponseMappableObjectBlock, Class<ARKMappableObject>))responseMappableObjectOnMainThread;
    
    // 返回JSON解析模型数组(目前默认是从JSON数据解析)
    // 同上,解析JSON数据,返回数据模型数组
    - (ARKRequest *(^)(ResponseMappableObjectArrayBlock, Class<ARKMappableObjectArray>))responseMappableObjectArrayOnMainThread;
    
    // 解析协议
    @protocol ARKMappableObject <NSObject>
    + (nullable id<ARKMappableObject>)objectWithResponseObject:(id)object;
    @end
    
    @protocol ARKMappableObjectArray <NSObject>
    + (nullable NSArray<id<ARKMappableObject>> *)arrayWithResponseObject:(id)object;
    @end
    

    请求例子

    假设请求一个JSON数据

    - (IBAction)startRequest:(UIButton *)sender {
      // 创建请求
      ARKRequest *request = [ARKRequest requestWithURLString:@"http://www.example.com/json" parameters:nil];
    
      // 如果self不持有request,那么block内的self可以不使用弱引用,因为block执行完毕后会清空
      // 相应的,执行该请求的对象(例如ViewController)生命周期将会被延长
      // 如果希望退出当前控制器时,控制器可以立即销毁,那么请在block里使用weak self
      request.requestWillResume(^(ARKRequest *request) {
        
          // 请求前的准备阶段,展示加载视图
          [self showLoadingView];
        
      }).requestProgressBlock(^(NSProgress *progress) {
        
          // 更新进度,requestProgressBlock不在主线程
          dispatch_async(dispatch_get_main_queue(), ^{
              self.progressView.progress = (float)progress.completedUnitCount / (float)progress.totalUnitCount;
          });
        
      }).responseJSONOnMainThread(^(NSURLSessionTask *task, id json, NSError *error) {
        
          // 隐藏加载视图
          [self hideLoadingView];
          NSLog(@"请求完毕, thread: %@", [NSThread currentThread]);
        
          if (error == nil) {
              // 请求成功
              [self requestSuccessWithJSON:json];
          } else {
              // 请求失败
              [self requestFailureWithError:error];
          }
    
      }).resume();
    }
    

    辅助类

    另外还有两个辅助类:

    • ARKRequestCacheManager
    • ARKNonRepetitiveRequestManager

    ARKRequestCacheManager用于缓存请求
    ARKNonRepetitiveRequestManager用于发起不重复的请求

    1、ARKRequestCacheManager(单例)

    使用ARKRequestCacheManager发起网络请求时,manager会缓存请求实例
    当请求完毕后,manager移除对应的请求实例
    可以通过该辅助类轻松实现暂停和取消请求

    如果是使用ARKRequestCacheManager发起的下载请求,当下载失败时,manager会从error.userInfo字典中,取出NSURLSessionDownloadTaskResumeData
    并且使用URL作为Key缓存在NSCache中,同时还会保存在磁盘Cache目录下

    当下次发起下载请求时,会根据URL判断本地是否有断点数据

    • 如果有断点数据,那么会调用 -downloadWithType:resumeData:destination: 进行断点续传
    • 如果没有断点数据,重新创建下载请求

    使用ARKRequestCacheManager发起网络请求

    - (void)startDownload {
        
        // 使用 ARKRequestCacheManager 发起下载请求
        // ARKRequestCacheManager 会保存这次请求,直到请求完毕
        // 当下载失败时,会保存这次的断点数据,下次发起时会优先读取断点数据进行断点续传
    
        ARKRequestCacheManager *manager = [ARKRequestCacheManager sharedManager];
        ARKRequest *request = [manager downloadWithType:ARKSessionTypeDefault 
                                              URLString:kURLString
                                             parameters:nil
                                            destination:^NSURL * _Nonnull(NSURL * _Nonnull location, NSURLResponse * _Nonnull response) {
            
            // 指定保存路径
            NSString *documentDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
            NSString *path = [documentDir stringByAppendingPathComponent:@"image.jpg"];
    
            // TODO: = =
            // 如果不是fileURL,那么最后response很可能会出错
            return [NSURL fileURLWithPath:path];
        }];
        
        
        __weak typeof(&*self) weakSelf = self;
        
        request.requestWillResume(^(ARKRequest *request) {
            
            // 显示加载视图
            [weakSelf showLoadingView];
            
        }).requestWillSuspend(^(ARKRequest *request) {
            
            NSLog(@"请求即将暂停,state: %ld", request.task.state);
            
        }).requestProgressBlock(^(NSProgress *progress) {
            
            // 更新进度条
            dispatch_async(dispatch_get_main_queue(), ^{
                weakSelf.progressView.progress = (float)progress.completedUnitCount / (float)progress.totalUnitCount;
            });
            
        }).responseDataOnMainThread(^(NSURLSessionTask *task, NSData *data, NSError *error) {
            
            // 隐藏加载视图
            [weakSelf hideLoadingView];
            
            NSLog(@"请求完毕, thread: %@", [NSThread currentThread]);
            
            if (error != nil) {
                [weakSelf showErrorMessage:[NSString stringWithFormat:@"下载失败, error:%@", error.localizedDescription]];
                return ;
            }
            
            if (data == nil || [data isKindOfClass:[NSNull class]]) {
                [weakSelf showErrorMessage:[NSString stringWithFormat:@"返回空数据"]];
                return ;
            }
            
            weakSelf.imageView.image = [UIImage imageWithData:data];
    
        }).resume();
    }
    
    // 暂停
    - (IBAction)suspendDownload {
        // 从 ARKRequestCacheManager 取出请求
        ARKRequest *request = [[ARKRequestCacheManager sharedManager] requestCacheWithURLString:kURLString];
        if (request != nil) {
            request.suspend();
            [self hideLoadingView];
        }
    }
    
    // 执行
    - (IBAction)resumeDownload {
        // 从 ARKRequestCacheManager 取出请求
        ARKRequest *request = [[ARKRequestCacheManager sharedManager] requestCacheWithURLString:kURLString];
    
        if (request != nil && (request.state == NSURLSessionTaskStateSuspended)) {
            // 继续请求
            request.resume()
            [self showLoadingView];
        } else {
            // 创建请求
            [self startDownload];
        }
    }
    

    2、ARKNonRepetitiveRequestManager(单例)

    ARKNonRepetitiveRequestManager 同样具有 ARKRequestCacheManage r的功能,并且使用 ARKNonRepetitiveRequestManager 发起多个相同的请求时,只允许最先发起的请求生效,其余请求均忽略

    使用ARKNonRepetitiveRequestManager发起网络请求

    - (IBAction)backgroundDownload:(UIButton *)sender {
        ARKNonRepetitiveRequestManager *manager = [ARKNonRepetitiveRequestManager sharedManager];
    
        // 可以保证只有最先发起的请求有效,不进行重复的请求
        ARKRequest *request = [manager downloadWithType:ARKSessionTypeBackground // 后台下载
                                              URLString:kImageURLString
                                             parameters:nil
                                            destination:^NSURL * _Nonnull(NSURL * _Nonnull location, NSURLResponse * _Nonnull response) {
            
            NSString *documentDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
            NSString *path = [documentDir stringByAppendingPathComponent:@"BackgrounDownloadImage.jpg"];
            return [NSURL fileURLWithPath:path];
        }];
        
        __weak typeof(&*self) weakSelf = self;
        
        request.responseDataOnMainThread(^(NSURLSessionTask *task, NSData *data, NSError *error) {
            
            NSLog(@"下载完毕, thread: %@", [NSThread currentThread]);
            
            if (data.length > 0) {
                weakSelf.imageView.image = [UIImage imageWithData:data];
            }
    
        }).didFinishEventsForBackgroundURLSession(^(NSURLSession *session) {
            NSLog(@"后台下载完毕");
            
            AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
            void(^completionHandler)() = delegate.didFinishEventsForBackgroundURLSession;
            delegate.didFinishEventsForBackgroundURLSession = nil;
            
            // 通知系统已接收到后台事件,清除后台网络会话标识
            completionHandler();
            
        }).resume();
    }
    

    其他

    目前OC的框架中网络层仍然是 -request:success:failure:的调用形式(毕竟成熟,调用形式更普及 = =)
    这个网络模块的封装纯属一时兴起,还有很多问题没有考虑,例如安全机制问题等等
    总之就是有待完善

    欢迎大家提出建议

    相关文章

      网友评论

          本文标题:iOS 网络请求模块封装 - OC

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