美文网首页
AFNetworkReachabilityManager 监控网

AFNetworkReachabilityManager 监控网

作者: 林大鹏 | 来源:发表于2017-09-12 19:54 被阅读127次

主要参考:
AFNetworking 全解析之RechabilityManager
AFNetworkReachabilityManager 监控网络状态

AFNetworkReachabilityManager 是对 systemConfiguration 模块的封装,跟苹果文档中Reachability 的实现基本一致,本节我们将以AFNetworkingAFNetworkReachabilityManager 为例来说明在iOS开发中,如何实现网络监控状态.

一. AFNetworkReachabilityManager 的使用和实现

AFNetworkReachabilityManager对象的networkReachabilityStatus属性标记当前的网络状态,有以下几种选项

AFNetworkReachabilityStatusUnknown 未知的网络状态
AFNetworkReachabilityStatusNotReachable 当前处于一个无网的状态,或者被禁用的网络状态
AFNetworkReachabilityStatusReachableViaWWAN 当前网络使用移动
AFNetworkReachabilityStatusReachableViaWiFi 当前使用wifi连接网络

AFNetworkReachabilityManager的使用是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控。

- 初始化 AFNetworkReachabilityManager

- 调用 startMonitoring 方法开始对网络状态进行监控 

- [设置 networkReachabilityStatusBlock 在每次网络状态改变时, 调用这个 block

初始化 AFNetworkReachabilityManager

在初始化方法中,使用 SCNetworkReachabilityCreateWithAddress 或者 SCNetworkReachabilityCreateWithName 生成一个 SCNetworkReachabilityRef 的引用。

+ (instancetype)managerForDomain:(NSString *)domain {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);

    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];

    return manager;
}

+ (instancetype)managerForAddress:(const void *)address {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
    AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];

    return manager;
}
  1. 这两个方法会通过一个域名或者一个 sockaddr_in 的指针生成一个 SCNetworkReachabilityRef

  2. 调用- [AFNetworkReachabilityManager initWithReachability:] 将生成的 SCNetworkReachabilityRef 引用传给 networkReachability

  3. 设置一个默认的 networkReachabilityStatus

     - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
         self = [super init];
         if (!self) {
             return nil;
         }
       
         self.networkReachability = CFBridgingRelease(reachability);
         self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
    
         return self;
     }
    

当调用CFBridgingRelease(reachability)后,会把reachability 桥接成一个NSObject 对象赋值给self.networkReachability,然后释放原来的CoreFoundation对象。

监控网络状态

在初始化 AFNetworkReachabilityManager 后,会调用 startMonitoring方法开始监控网络状态。

- (void)startMonitoring {
    [self stopMonitoring];

    if (!self.networkReachability) {
        return;
    }

    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;

        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };

    id networkReachability = self.networkReachability;
    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
    SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
}

 - (void)stopMonitoring {
     if (!self.networkReachability) {
         return;
     }

     SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
 }
  1. 先调用 - stopMonitoring 方法,如果之前设置过对网络状态的监听,使用 SCNetworkReachabilityUnscheduleFromRunLoop 方法取消之前在 Main Runloop 中的监听

  2. 创建一个在每次网络状态改变时的回调,每次回调被调用时,重新设置networkReachabilityStatus 属性,调用networkReachabilityStatusBlock

  3. 创建一个 SCNetworkReachabilityContext

typedef struct {
    CFIndex     version;
    void *      __nullable info;
    const void  * __nonnull (* __nullable retain)(const void *info);
    void        (* __nullable release)(const void *info);
    CFStringRef __nonnull (* __nullable copyDescription)(const void *info);
 } SCNetworkReachabilityContext;

 SCNetworkReachabilityContext context = {
     0,
     (__bridge void *)callback,
     AFNetworkReachabilityRetainCallback, 
     AFNetworkReachabilityReleaseCallback, 
     NULL
 };
  • 其中的 callback 就是上一步中的创建的 block 对象

  • 这里的 AFNetworkReachabilityRetainCallbackAFNetworkReachabilityReleaseCallback 都是非常简单的 block,在回调被调用时,只是使用 Block_copyBlock_release 这样的宏

  • 传入的 info 会以参数的形式在 AFNetworkReachabilityCallback 执行时传入

    static const void * AFNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info); }
    
    static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); } }
    
  1. 当目标的网络状态改变时,会调用传入的回调

    SCNetworkReachabilitySetCallback(
        (__bridge SCNetworkReachabilityRef)networkReachability,
        AFNetworkReachabilityCallback, 
        &context
    );
    
  2. Main Runloop 中对应的模式开始监控网络状态

     SCNetworkReachabilityScheduleWithRunLoop(
          (__bridge SCNetworkReachabilityRef)networkReachability, 
          CFRunLoopGetMain(), 
          kCFRunLoopCommonModes
    );
    
  3. 获取当前的网络状态,调用 callback

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
          SCNetworkReachabilityFlags flags;
          if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
                AFPostReachabilityStatusChange(flags, callback);
          }
      });
    

设置 networkReachabilityStatusBlock 以及回调

Main Runloop 中对网络状态进行监控之后,在每次网络状态改变,就会调用AFNetworkReachabilityCallback 函数:

static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
    AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}

这里会从info 中取出之前存在 context 中的 AFNetworkReachabilityStatusBlock

__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
    __strong __typeof(weakSelf)strongSelf = weakSelf;

    strongSelf.networkReachabilityStatus = status;
    if (strongSelf.networkReachabilityStatusBlock) {
        strongSelf.networkReachabilityStatusBlock(status);
    }
};

取出这个 block 之后,传入 AFPostReachabilityStatusChange 函数:

static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
    dispatch_async(dispatch_get_main_queue(), ^{
        if (block) {
            block(status);
        }
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
        [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
    });
}
  1. 调用 AFNetworkReachabilityStatusForFlags 获取当前的网络可达性状态
  2. 在主线程中异步执行上面传入的 callback block(设置 self 的网络状态,调用 networkReachabilityStatusBlock
  3. 发送 AFNetworkingReachabilityDidChangeNotification 通知.
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
    BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
    BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
    BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
    BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
    BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));

    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
    if (isNetworkReachable == NO) {
        status = AFNetworkReachabilityStatusNotReachable;
    }
#if TARGET_OS_IPHONE
    else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
        status = AFNetworkReachabilityStatusReachableViaWWAN;
    }
#endif
    else {
        status = AFNetworkReachabilityStatusReachableViaWiFi;
    }

    return status;
}

因为 flags 是一个 SCNetworkReachabilityFlags,它的不同位代表了不同的网络可达性状态,通过 flags 的位操作,获取当前的状态信息 AFNetworkReachabilityStatus

typedef CF_OPTIONS(uint32_t, SCNetworkReachabilityFlags) {
    kSCNetworkReachabilityFlagsTransientConnection  = 1<<0,
    kSCNetworkReachabilityFlagsReachable        = 1<<1,
    kSCNetworkReachabilityFlagsConnectionRequired   = 1<<2,
    kSCNetworkReachabilityFlagsConnectionOnTraffic  = 1<<3,
    kSCNetworkReachabilityFlagsInterventionRequired = 1<<4,
    kSCNetworkReachabilityFlagsConnectionOnDemand   = 1<<5,     // __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_3_0)
    kSCNetworkReachabilityFlagsIsLocalAddress   = 1<<16,
    kSCNetworkReachabilityFlagsIsDirect     = 1<<17,
#if TARGET_OS_IPHONE
    kSCNetworkReachabilityFlagsIsWWAN       = 1<<18,
#endif  // TARGET_OS_IPHONE

    kSCNetworkReachabilityFlagsConnectionAutomatic  = kSCNetworkReachabilityFlagsConnectionOnTraffic
};

这里就是在 SystemConfiguration 中定义的全部的网络状态的标志位。

二. AFNetworkReachabilityManager 调用流程

UML结构图:

UML结构图.png

调用流程图:
这个流程图标识了AFReachability使用SCNetworkReachability并产生回调的过程

image.png

三. 与 AFNetworking 协作

其实这个类与 AFNetworking 整个框架并没有太多的耦合。正相反,它在整个框架中作为一个即插即用的类使用,每一个 AFURLSessionManager都会持有一个 AFNetworkReachabilityManager 的实例。

self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];

这是整个框架中除了 AFNetworkReachabilityManager.h/m 文件,唯一一个引用到这个类的地方。

在实际的使用中,我们也可以直接操作 AFURLSessionManagerreachabilityManager 来获取当前的网络可达性状态,而不是自己手动初始化一个实例,当然这么做也是没有任何问题的。

四.总结

  1. AFNetworkReachabilityManager 实际上只是一个对底层 SystemConfiguration 库中的 C 函数封装的类,它为我们隐藏了 C 语言的实现,提供了统一的 Objective-C 语言接口

  2. 它是AFNetworking 中一个即插即用的模块,具有很好的独立性。

相关文章

网友评论

      本文标题:AFNetworkReachabilityManager 监控网

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