美文网首页AFNetworking源码探究
AFNetworking源码探究(六) —— AFURLSess

AFNetworking源码探究(六) —— AFURLSess

作者: 刀客传奇 | 来源:发表于2018-02-28 21:52 被阅读70次

    版本记录

    版本号 时间
    V1.0 2018.02.28

    前言

    我们做APP发起网络请求,都离不开一个非常有用的框架AFNetworking,可以说这个框架的知名度已经超过了苹果的底层网络请求部分,很多人可能不知道苹果底层是如何发起网络请求的,但是一定知道AFNetworking,接下来几篇我们就一起详细的解析一下这个框架。感兴趣的可以看上面写的几篇。
    1. AFNetworking源码探究(一) —— 基本介绍
    2. AFNetworking源码探究(二) —— GET请求实现之NSURLSessionDataTask实例化(一)
    3. AFNetworking源码探究(三) —— GET请求实现之任务进度设置和通知监听(一)
    4. AFNetworking源码探究(四) —— GET请求实现之代理转发思想(一)
    5. AFNetworking源码探究(五) —— AFURLSessionManager中NSURLSessionDelegate详细解析(一)

    回顾

    上一篇主要讲了NSURLSessionDelegate中的三个代理方法,讲述了它们的使用场景以及用法,这一篇主要讲AFURLSessionManager中代理NSURLSessionTaskDelegate详细解析。


    1. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler

    首先看一下系统的API接口

    /* An HTTP request is attempting to perform a redirection to a different
     * URL. You must invoke the completion routine to allow the
     * redirection, allow the redirection with a modified request, or
     * pass nil to the completionHandler to cause the body of the redirection 
     * response to be delivered as the payload of this request. The default
     * is to follow redirections. 
     *
     * For tasks in background sessions, redirections will always be followed and this method will not be called.
     */
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                         willPerformHTTPRedirection:(NSHTTPURLResponse *)response
                                         newRequest:(NSURLRequest *)request
                                  completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;
    

    下面看一下AFN中该代理方法的实现

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest *))completionHandler
    {
        NSURLRequest *redirectRequest = request;
    
        if (self.taskWillPerformHTTPRedirection) {
            redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
        }
    
        if (completionHandler) {
            completionHandler(redirectRequest);
        }
    }
    

    这里,主要做了下面几个逻辑:

    • 如果有对应的block taskWillPerformHTTPRedirection,那么就调用self.taskWillPerformHTTPRedirection(session, task, response, request),返回一个新的request。
    • 接着就是利用生成的request重新请求
    if (completionHandler) {
            completionHandler(redirectRequest);
        }
    
    • 这个方法是在服务器去重定向的时候,才会被调用。
    • 此方法只会在default session或者ephemeral session中调用,而在background session中,session task会自动重定向。
    • 补充一点,初始化NSURLSession对象的时候需要使用NSURLSessionConfiguration。有三个类工厂方法:
      • + defaultSessionConfiguration 返回一个标准的 configuration,具有相同的共享 NSHTTPCookieStorage,共享 NSURLCache 和共享NSURLCredentialStorage
      • + ephemeralSessionConfiguration 返回一个预设配置,这个配置中不会对缓存,Cookie 和证书进行持久性的存储。这对于实现像秘密浏览这种功能来说是很理想的。
      • + backgroundSessionConfiguration:(NSString *)identifier 的独特之处在于,它会创建一个后台 session。后台 session 不同于常规的,普通的 session,它甚至可以在应用程序挂起,退出或者崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程(daemon)提供上下文。

    2. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler

    首先看一下苹果的API接口

    /* The task has received a request specific authentication challenge.
     * If this delegate is not implemented, the session specific authentication challenge
     * will *NOT* be called and the behavior will be the same as using the default handling
     * disposition. 
     */
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                                didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                                  completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
    

    下面看一下AFN中该方法的实现

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __block NSURLCredential *credential = nil;
    
        if (self.taskDidReceiveAuthenticationChallenge) {
            disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
        } else {
            if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
                if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                    credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                } else {
                    disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
                }
            } else {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
        }
    
        if (completionHandler) {
            completionHandler(disposition, credential);
        }
    }
    

    这里我们需要知道:

    • 这个是non-session-level级别的认证
    • 多了一个参数task,然后调用我们自定义的Block会多回传这个task作为参数,这样我们就可以根据每个task去自定义我们需要的https认证方式。

    3. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler

    首先看一下苹果的API接口

    /* Sent if a task requires a new, unopened body stream.  This may be
     * necessary when authentication has failed for any request that
     * involves a body stream. 
     */
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                                  needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler;
    

    看一下AFN对该方法的实现

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
     needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
    {
        NSInputStream *inputStream = nil;
    
        if (self.taskNeedNewBodyStream) {
            inputStream = self.taskNeedNewBodyStream(session, task);
        } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
            inputStream = [task.originalRequest.HTTPBodyStream copy];
        }
    
        if (completionHandler) {
            completionHandler(inputStream);
        }
    }
    

    该代理方法会在下面两种情况被调用

    • 如果task是由uploadTaskWithStreamedRequest:创建的,那么提供初始的request body stream时候会调用该代理方法。
    • 因为认证挑战或者其他可恢复的服务器错误,而导致需要客户端重新发送一个含有body stream的request,这时候会调用该代理。

    4. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;

    首先看一下苹果的API接口

    /* Sent periodically to notify the delegate of upload progress.  This
     * information is also available as properties of the task.
     */
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                                    didSendBodyData:(int64_t)bytesSent
                                     totalBytesSent:(int64_t)totalBytesSent
                           totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
    

    看一下AFN对该方法的实现

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
       didSendBodyData:(int64_t)bytesSent
        totalBytesSent:(int64_t)totalBytesSent
    totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
    {
    
        int64_t totalUnitCount = totalBytesExpectedToSend;
        if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
            NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
            if(contentLength) {
                totalUnitCount = (int64_t) [contentLength longLongValue];
            }
        }
    
        if (self.taskDidSendBodyData) {
            self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
        }
    }
    

    就是每次发送数据给服务器,会回调这个方法,通知已经发送了多少。


    5. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error;

    首先看一下苹果的API接口

    /* Sent as the last message related to a specific task.  Error may be
     * nil, which implies that no error occurred and this task is complete. 
     */
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                               didCompleteWithError:(nullable NSError *)error;
    

    看一下AFN对该方法的实现

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
        // delegate may be nil when completing a task in the background
        if (delegate) {
            [delegate URLSession:session task:task didCompleteWithError:error];
    
            [self removeDelegateForTask:task];
        }
    
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    

    这个代理就是task完成了的回调,这里涉及到task和代理的绑定与接触绑定,代码不难,就不多说了。

    后记

    本篇主要介绍了NSURLSessionTaskDelegate中五个代理方法的实现及其使用场景和注意事项。

    相关文章

      网友评论

        本文标题:AFNetworking源码探究(六) —— AFURLSess

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