AFNetworkReachabilityManager
是对 SystemConfiguration
模块的封装,苹果的文档中也有一个类似的项目 Reachability 这里对网络状态的监控跟苹果官方的实现几乎是完全相同的。
同样在 github 上有一个类似的项目叫做 Reachability 不过这个项目由于命名的原因可能会在审核时被拒绝。
无论是 AFNetworkReachabilityManager
,苹果官方的项目或者说 github 上的 Reachability,它们的实现都是类似的,而在这里我们会以 AFNetworking
中的 AFNetworkReachabilityManager
为例来说明在 iOS 开发中,我们是怎样监控网络状态的。
AFNetworkReachabilityManager 的使用和实现
AFNetworkReachabilityManager
的使用还是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控。
初始化 AFNetworkReachabilityManager
调用 startMonitoring
方法开始对网络状态进行监控
设置 networkReachabilityStatusBlock
在每次网络状态改变时, 调用这个 block
初始化 AFNetworkReachabilityManager
在初始化方法中,使用 SCNetworkReachabilityCreateWithAddress
或者 SCNetworkReachabilityCreateWithName
生成一个 SCNetworkReachabilityRef
的引用。
Objective-C
+ (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;
}
复制代码
这两个方法会通过一个域名或者一个 sockaddr_in
的指针生成一个 SCNetworkReachabilityRef
调用 - [AFNetworkReachabilityManager initWithReachability:]
将生成的 SCNetworkReachabilityRef
引用传给 networkReachability
设置一个默认的 networkReachabilityStatus
Objective-C
- (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
方法开始监控网络状态。
Objective-C
- (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);
}
});
}
复制代码
先调用 - stopMonitoring
方法,如果之前设置过对网络状态的监听,使用 SCNetworkReachabilityUnscheduleFromRunLoop
方法取消之前在 Main Runloop 中的监听
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
复制代码
创建一个在每次网络状态改变时的回调
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
复制代码
* 每次回调被调用时
* 重新设置 `networkReachabilityStatus` 属性
* 调用 `networkReachabilityStatusBlock`
复制代码
创建一个 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 对象
* 这里的 `AFNetworkReachabilityRetainCallback` 和 `AFNetworkReachabilityReleaseCallback` 都是非常简单的 block,在回调被调用时,只是使用 `Block_copy` 和 `Block_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);
}
}
复制代码
当目标的网络状态改变时,会调用传入的回调
SCNetworkReachabilitySetCallback(
(__bridge SCNetworkReachabilityRef)networkReachability,
AFNetworkReachabilityCallback,
&context
);
复制代码
在 Main Runloop 中对应的模式开始监控网络状态
SCNetworkReachabilityScheduleWithRunLoop(
(__bridge SCNetworkReachabilityRef)networkReachability,
CFRunLoopGetMain(),
kCFRunLoopCommonModes
);
复制代码
获取当前的网络状态,调用 callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
复制代码
在下一节中会介绍上面所提到的一些 C 函数以及各种回调。
设置 networkReachabilityStatusBlock 以及回调
在 Main Runloop 中对网络状态进行监控之后,在每次网络状态改变,就会调用 AFNetworkReachabilityCallback
函数:
Objective-C
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
复制代码
这里会从 info
中取出之前存在 context
中的 AFNetworkReachabilityStatusBlock
。
Objective-C
__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
函数:
Objective-C
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];
});
}
复制代码
调用 AFNetworkReachabilityStatusForFlags
获取当前的网络可达性状态
在主线程中异步执行上面传入的 callback
block(设置 self
的网络状态,调用 networkReachabilityStatusBlock
)
发送 AFNetworkingReachabilityDidChangeNotification
通知.
Objective-C
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
。
Objective-C
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
中定义的全部的网络状态的标志位。
与 AFNetworking 协作
其实这个类与 AFNetworking
整个框架并没有太多的耦合。正相反,它在整个框架中作为一个即插即用的类使用,每一个 AFURLSessionManager
都会持有一个 AFNetworkReachabilityManager
的实例。
Objective-C
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
复制代码
这是整个框架中除了 AFNetworkReachabilityManager.h/m
文件,唯一一个引用到这个类的地方。
在实际的使用中,我们也可以直接操作 AFURLSessionManager
的 reachabilityManager
来获取当前的网络可达性状态,而不是自己手动初始化一个实例,当然这么做也是没有任何问题的。
总结
AFNetworkReachabilityManager
实际上只是一个对底层 SystemConfiguration
库中的 C 函数封装的类,它为我们隐藏了 C 语言的实现,提供了统一的 Objective-C 语言接口
它是 AFNetworking
中一个即插即用的模块
本文转载自 Draveness 技术博客。
原文链接:https://draveness.me/afnetworking4
评论