AFNetworkReachabilityManager 監控網絡狀態(四)
AFNetworkReachabilityManager 是對 SystemConfiguration 模塊的封裝,蘋果的文檔中也有一個類似的項目 Reachability 這里對網絡狀態的監控跟蘋果官方的實現幾乎是完全相同的。
同樣在 github 上有一個類似的項目叫做 Reachability 不過這個項目 由于命名的原因可能會在審核時被拒絕 。
無論是 AFNetworkReachabilityManager ,蘋果官方的項目或者說 github 上的 Reachability,它們的實現都是類似的,而在這里我們會以 AFNetworking 中的 AFNetworkReachabilityManager 為例來說明在 iOS 開發中,我們是怎樣監控網絡狀態的。
AFNetworkReachabilityManager 的使用和實現
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; } </pre>
- 這兩個方法會通過一個 域名 或者一個 sockaddr_in 的指針生成一個 SCNetworkReachabilityRef
- 調用 - [AFNetworkReachabilityManager initWithReachability:] 將生成的 SCNetworkReachabilityRef 引用傳給 networkReachability
- 設置一個默認的 networkReachabilityStatus
(instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { self = [super init]; if (!self) {
return nil;
}
self.networkReachability = CFBridgingRelease(reachability); self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self; } </pre>
當調用 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); }
}); } </pre>
-
先調用 - stopMonitoring 方法,如果之前設置過對網絡狀態的監聽,使用 SCNetworkReachabilityUnscheduleFromRunLoop 方法取消之前在 Main Runloop 中的監聽
- (void)stopMonitoring {
if (!self.networkReachability) {
}return;
-
</pre>
SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);}</li>
創建一個在每次網絡狀態改變時的回調
weak typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { strong typeof(weakSelf)strongSelf = weakSelf;</pre>
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 }; </pre>
- 其中的 callback 就是上一步中的創建的 block 對象
- 這里的 AFNetworkReachabilityRetainCallback 和 AFNetworkReachabilityReleaseCallback 都是非常簡單的 block,在回調被調用時,只是使用 Block_copy 和 Block_release 這樣的宏
-
傳入的 info 會以參數的形式在 AFNetworkReachabilityCallback 執行時傳入
static const void
static void AFNetworkReachabilityReleaseCallback(const void
info) { if (info) { Block_release(info); } }</p> </li> </ul> </li>當目標的網絡狀態改變時,會調用傳入的回調
SCNetworkReachabilitySetCallback( (
-
在 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); } });
</ol>
在下一節中會介紹上面所提到的一些 C 函數以及各種回調。
設置 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); }
}; </pre>
取出這個 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]; }); }
- 調用 AFNetworkReachabilityStatusForFlags 獲取當前的網絡可達性狀態
- 在主線程中異步執行 上面傳入的 callback block(設置 self 的網絡狀態,調用 networkReachabilityStatusBlock )
- 發送 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;
} </pre>
因為 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
}; </pre>
這里就是在 SystemConfiguration 中定義的全部的網絡狀態的標志位。
與 AFNetworking 協作
其實這個類與 AFNetworking 整個框架并沒有太多的耦合。正相反,它在整個框架中作為一個 即插即用 的類使用,每一個 AFURLSessionManager 都會持有一個 AFNetworkReachabilityManager 的實例。
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];這是整個框架中除了 AFNetworkReachabilityManager.h/m 文件, 唯一一個 引用到這個類的地方。
在實際的使用中,我們也可以直接操作 AFURLSessionManager 的 reachabilityManager 來獲取當前的網絡可達性狀態,而不是自己手動初始化一個實例,當然這么做也是沒有任何問題的。
總結
- AFNetworkReachabilityManager 實際上只是一個對底層 SystemConfiguration 庫中的 C 函數封裝的類,它為我們隱藏了 C 語言的實現,提供了統一的 Objective-C 語言接口
- 它是 AFNetworking 中一個即插即用的模塊
來自: http://draveness.me/afnetworking4/