概述
在AFN框架中,AFURLSessionManager对象的初始化方法中创建了AFNetworkReachabilityManager对象用于监听设备当前连接网络的状态。文本分析一下AFNetworkReachabilityManager。
初始化方法
AFNetworkReachabilityManager提供了4种创建方法,如下:
+ (instancetype)sharedManager; //创建单例对象
+ (instancetype)manager; //创建实例对象
+ (instancetype)managerForDomain:(NSString *)domain; //根据地址名创建实例对象
+ (instancetype)managerForAddress:(const void *)address; //根据sockaddr创建实例对象
第一个方法创建单例对象,代码注释如下:
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager]; //调用manager方法创建对象
});
return _sharedManager;
}
sharedManager调用manager方法创建一个AFNetworkReachabilityManager对象,代码注释如下:
+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address; //创建一个ipv6类型的地址结构体
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address; //创建一个ipv4类型的地址结构体
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address]; //根据地址信息创建对象
}
其中sockaddr_in6和sockaddr_in是描述网络套接字的结构体,包含协议族类型、端口、ip地址等信息,然后调用managerForAddress:方法创建,代码注释如下:
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); //创建SCNetworkReachabilityRef对象
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; //创建AFNetworkReachabilityManager对象,初始还参数
return manager;
}
该方法的核心代码是通过SCNetworkReachabilityCreateWithAddress方法创建一个SCNetworkReachabilityRef对象reachability,该对象负责监听address的网络状态,address参数是需要监听的地址信息,由于address参数的ip地址为空,则reachability对象监听当前设备的网络连接状态。另一个方法managerForDomain:是通过传进来的domain网络地址名,例如www.baidu.com来创建。代码注释如下:
+ (instancetype)managerForDomain:(NSString *)domain {
//根据domain创建SCNetworkReachabilityRef对象
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; //创建AFNetworkReachabilityManager对象,初始还参数
return manager;
}
SCNetworkReachabilityCreateWithName方法和上面的类似,也是用于创建一个SCNetworkReachabilityRef对象。调用initWithReachability:方法,初始化参数,代码注释如下:
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
self.networkReachability = CFBridgingRelease(reachability); //初始化networkReachability属性
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; //初始化连接状态属性
return self;
}
networkReachability属性持有reachability,networkReachabilityStatus属性表示当前连接的网络状态,共有以下几种:
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,//未知状态
AFNetworkReachabilityStatusNotReachable = 0, //未连接
AFNetworkReachabilityStatusReachableViaWWAN = 1, //蜂窝移动网络(2G/3G/4G)
AFNetworkReachabilityStatusReachableViaWiFi = 2, //wifi网络
};
上面的几个枚举值表示当前检测到的网络状态。
监听网络状态
AFNetworkReachabilityManager通过startMonitoring方法和stopMonitoring开始并停止监听当前设备连接的网络状态。
-
startMonitoring方法
该方法主要通过SystemConfiguration框架提供的API将networkReachability让对象加入runloop中,开始工作,并且绑定监听的回调函数处理状态改变。代码注释如下:
- (void)startMonitoring { [self stopMonitoring]; //1.停止之前的监听 if (!self.networkReachability) { return; } //2.创建context的block __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对象 SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; //3.设置networkReachability的回调函数和context SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context); //4.将networkReachability加入runloop中 SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); //5.获取当前网络连接状态 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); //执行block,发通知 } }); }
该方法首先停止之前的监听,然后调用SCNetworkReachabilitySetCallback方法来设置networkReachability的回调函数AFNetworkReachabilityCallback和上下文context对象,该方法的定义和参数如下:
Boolean SCNetworkReachabilitySetCallback ( SCNetworkReachabilityRef target, //networkReachability对象 SCNetworkReachabilityCallBack __nullable callout, //回调方法 SCNetworkReachabilityContext * __nullable context //上下文兑现 )
首先target对象是要绑定的networkReachability对象,即networkReachability属性,callout是networkReachability对象监听到网络状态发生改变时,触发的回调函数,类型是SCNetworkReachabilityCallBack,定义如下:
typedef void (*SCNetworkReachabilityCallBack) ( SCNetworkReachabilityRef target, //networkReachability对象 SCNetworkReachabilityFlags flags, //回调参数,状态值 void * __nullable info //info函数指针 );
该函数回抛target对象,即之前绑定的networkReachability对象。以及flag标识,通过flag可以获取当前网络状态值。以及info指针,指向一个block。其中info是由context提供的,context是SCNetworkReachabilityContext类型的结构体,定义如下:
typedef struct { CFIndex version; void * __nullable info; //info函数指针 const void * __nonnull (* __nullable retain)(const void *info); //retain函数指针 void (* __nullable release)(const void *info); //release函数指针 CFStringRef __nonnull (* __nullable copyDescription)(const void *info); //获取info的Description的函数指针 } SCNetworkReachabilityContext;
其中info函数指针提供给上文的回调函数使用,作为回调参数info。info是AFNetworkReachabilityStatusBlock类型,需要一个回调参数status,创建info的代码如下:
__weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; //设置状态值 if (strongSelf.networkReachabilityStatusBlock) { //将状态值通过block回抛给外界 strongSelf.networkReachabilityStatusBlock(status); } };
该函数负责将获取到的网络状态值status通过networkReachabilityStatusBlock回抛给外界。retain函数指针和release函数指针也分别设置了。
callout的方法如下:
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); //调用AFPostReachabilityStatusChange方法 }
该方法调用AFPostReachabilityStatusChange方法做逻辑处理,注释如下:
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); //根据flags获取当前网络连接状态status dispatch_async(dispatch_get_main_queue(), ^{ if (block) { block(status); //block是context中的info指针,调用info将status传递给外界 } //status作为通知的值,发通知抛给外界 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; }); }
该方法首先根据系统回调的flags参数和AFNetworkReachabilityStatusForFlags方法,获取网络连接状态status,然后调用block,即之前context中的info指针,将status抛给外界。同时抛一个通知将status抛给外界。因此当网络状态改变时,会同时用两种方式传递给外界。
AFNetworkReachabilityStatusForFlags方法是核心方法,负责根据flag的状态值,转化为相应的枚举值AFNetworkReachabilityStatus。代码注释如下:
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) {//当前连接是WWAN status = AFNetworkReachabilityStatusReachableViaWWAN; } #endif else { //当前连接是WiFi status = AFNetworkReachabilityStatusReachableViaWiFi; } return status; }
最后在startMonitoring方法中还调用了SCNetworkReachabilityGetFlags方法获取当前网络状态,传递给外界。因为之前的设置是在网络状态发生变化时触发的。
-
stopMonitoring方法
通知监听的方法是让networkReachability对象从runloop中注销。代码注释如下:
- (void)stopMonitoring { if (!self.networkReachability) { return; } //从runloop中注销networkReachability对象 SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); }
读取网络状态
AFNetworkReachabilityManager维护了一些网络状态属性,如下:
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
外界通过isReachable方法、isReachableViaWWAN方法、isReachableViaWiFi方法和networkReachabilityStatus属性可以获取当前的网络状态。同时通过实现keyPathsForValuesAffectingValueForKey:方法设置属性值的依赖关系。当reachable、reachableViaWWAN、reachableViaWiFi这些属性的值发生变化时,会触发networkReachabilityStatus属性的kvo,如果外界通过kvo监听networkReachabilityStatus属性的变化,这时会触发kvo。
总结
AFNetworkReachabilityManager作为一个监听设备网络连接状态的类,核心是使用了systemConfiguration框架中的SCNetworkReachability相关类和API实现的,值得学习和了解。