美文网首页
来说说AFNetworking

来说说AFNetworking

作者: 卡丁车手 | 来源:发表于2017-10-30 23:01 被阅读74次

    大概分析一下AFNetworking功能和使用流程。

    使用苹果的 NSURLSession 发起一个 POST 请求大概是以下这种方式:

    // 创建NSMutableURLRequest对象,给它设置请求头、请求体
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:URL]];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[@"pram1=p1" dataUsingEncoding:NSUTF8StringEncoding]];
    
    // 创建NSURLSession对象,设置配置信息、代理和代理回调线程
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
    
    // 使用session去生成一个网络请求任务NSURLSessionDataTask,通过 block 或代理回调结果
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        NSLog(@"%@",dict);
    }];
    // 开启这个请求任务
    [task resume];
    

    AFNetworking 的请求方式:

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager POST:URL parameters:@{@"pram1":@"p1"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"%@",error);
    }];
    

    创建好AFHTTPSessionManager对象之后,直接进行 GET/POST 等请求。请求和响应都被封装了起来。

    AFNetworking的封装
    请求封装:

    AFURLRequestSerialization负责封装请求参数,通过传入的URL、请求类型、请求参数创建一个request ,并设置请求头,一般设置以下几个请求头参数:

    • Accept-Language:
      告诉服务器本地支持的语言类型。一般格式如下:
      Accept-Language zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3,zh-cn是简体中文,zh是中文,q的值越大表示越倾向该语言。
    • User-Agent:
      用户代理,简称UA。它里边包含了一些本地信息,如浏览器类型、操作系统及版本、CPU 类型等,可自行添加其它需要的信息,如网络环境。
    • Authorization :
      这个是可选项,如果访问的服务器是通过Basic Auth进行身份验证的,可以把账号密码通过它添加进请求头中。一般都使用https,因为http直接暴露了账号密码。
    • Content-Type:
      常用的 Content-Type 有 以下几种:
      text/plain; charset=utf-8,表示请求体是纯文本格式,utf-8编码;
      application/json; charset=utf-8,表示请求体是JSON字符串,如{"key":"value"};
      application/x-www-form-urlencoded; charset=utf-8,表示请求体是键值对格式,如"key"="value",这是AFNetworking默认的请求体格式;
      multipart/form-data; charset=utf-8; boundary=Boundary_XX,表示上传文件,一般上传的文件都比较大,需要分割,boundary表示分割符。

    AF中共有三个类负责创建 request :
    AFHTTPRequestSerializer
    AFJSONRequestSerializer
    AFPropertyListRequestSerializer

    1、默认是通过 AFHTTPRequestSerializer 创建,它会把通过字典传入的参数转换为键值对的形式 key1=value1&key2=value2,key 和 value 都是经过编码的。

    如果是 GET 请求,参数会通过 ?直接拼接在 URL 后面,如:http://localhost?key1=value1&key2=value2

    如果 POST 请求,AF 会把 Content-Type 设置为 application/x-www-form-urlencoded; charset=utf-8,然后把参数通过 UTF-8 编码后放入请求体中。

    2、若通过 AFJSONRequestSerializer 创建,GET 请求与上面相同,POST 请求时,AF 会把 Content-Type 设置为 application/json; charset=utf-8,参数直接使用NSJSONSerialization进行序列化,然后放入请求体中。

    3、若通过 AFPropertyListRequestSerializer 创建,Content-Type 设置为 application/x-plist,参数通过NSPropertyListSerialization序列化后放入请求体。

    开发中用的最多的是前两种序列化方式,第三种我还从来没用过。

    上传文件:

    上传文件有严格的格式要求:

    (开头分割线 "--" 加 "boundary" ,此处的boundary是Boundaryxxx)
    --Boundaryxxx 
    (文件参数)
    Content-Disposition: form-data; name="file"; filename="picture.png" 
    (文件类型)
    Content-Type: image/png
    (文件二进制数据)
    ...png datas...
    (结束分割线 "--" 加 "boundary" 加 "--")
    --Boundaryxxx--
    

    其中第二步的 form-data 就像 application/json 一样表示上传格式,name 是接口参数,比如服务器要求上传文件的参数名为 "abc",就要设置 name="abc",filename 是文件的名字。

    若有多个文件,重复前四步即可。如前面所说,上传文件要给request设置请求头 Content-Type 为 multipart/form-data; boundary=Boundaryxxx。boundary 就是分割数据时设置的标示。

    AF 提供了几个不同的上传方法。
    (具体的文件读取有些麻烦,以后再写)

    未完待续。。。

    响应封装:

    AFURLResponseSerialization类负责响应数据处理。在 AFURLSessionManager 中创建好 dataTask 后会为这个 dataTask 设置代理AFURLSessionManagerTaskDelegate,每个 AFURLSessionManager 都有一个代理。

    NSURLSession 可以添加大量的 NSURLSessionTask 在后台下载,想要检查某个任务进度,只需 double taskProgress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;就能计算出来。

    dataTask 的数据接收和接收完毕代理方法都会被转接到代理类中,代理对象会创建观察者监听 dataTask (NSURLSessionTask对象) 的几个属性,当发送/接收到数据时、任务状态改变时,这些数据会发生变化,这样就能监听到 上传/下载进度 和 取消/开启任务。

    请求完毕后使用responseSerializer开始对结果进行解析,AF 默认使用 AFJSONResponseSerializer 类解析,它的默认可解析类型有:@"application/json", @"text/json", @"text/javascript"。

    一般情况下我们的服务器返回的都是 JSON 数据,不过有时候也会返回一些其他类型的数据,导致 AF 不能被解析而返回错误,这就有了网上的一些解决办法:

    AFHTTPSessionManager *manager =[AFHTTPSessionManager manager];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",@"text/plain", nil];
    

    十分地简单粗暴😆。

    除了 AFJSONResponseSerializer,还有一些其它解析类:
    AFHTTPResponseSerializer,直接返回 NSData 数据;
    AFXMLParserResponseSerializer,使用 NSXMLParser 进行解析
    AFPropertyListResponseSerializer,使用 NSPropertyListSerialization 进行解析;
    AFImageResponseSerializer,解析成 Image。

    解析完成后,通过completionHandler回调结果。

    平时最常用的 GET/POST 请求流程说完了,说下其它功能吧。

    网络监听:

    AFNetworkReachabilityManager 类用来监听网络变化,可以单独拉出来使用。它的原理跟苹果官方的 Reachability 相同,都是用 SCNetworkReachability 来进行监听的。

    不过 Reachability 并没有那么强大,苹果解释说它并不能真正检测到程序是否可以连接到服务器,只能检测到接口是否可连接,以及接口的网络类型。

    也就是说,程序只连接到路由器,而路由器没有连接到外网,是检测不出来的;网络信号差,无法真正和服务器连接通信,也检测不出来。网上有人给出一种解决方案:发送一个真实的网络请求,或者使用socket编程,建立一个tcp链接来检测(三次握手成功),只要链接成功则服务器可达。这样只会发送tcpip的报头,数据量最小。如果网络环境差,connect函数会阻塞,所以最好不要在主线程下,调用示例代码:

    #import <arpa/inet.h>
    /// 服务器可达返回true
    - (BOOL)socketReachabilityTest {
        // 客户端 AF_INET:ipv4  SOCK_STREAM:TCP链接
        int socketNumber = socket(AF_INET, SOCK_STREAM, 0);
        // 配置服务器端套接字
        struct sockaddr_in serverAddress;
        // 设置服务器ipv4
        serverAddress.sin_family = AF_INET;
        // 百度的ip
        serverAddress.sin_addr.s_addr = inet_addr("202.108.22.5");
        // 设置端口号,HTTP默认80端口
        serverAddress.sin_port = htons(80);
        if (connect(socketNumber, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)) == 0) {
            close(socketNumber);
            return true;
        }
        close(socketNumber);;
        return false;
    }
    

    GitHub上也有检测网络真实环境的轮子如RealReachability

    未完待续。。。

    相关文章

      网友评论

          本文标题:来说说AFNetworking

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