把握行业变革关键节点,12 月 19 日 - 20 日,AICon北京站即将重磅启幕! 了解详情
写点什么

AFNetworkReachabilityManager 监控网络状态(四)

  • 2019-12-10
  • 本文字数:5547 字

    阅读完需:约 18 分钟

AFNetworkReachabilityManager 监控网络状态(四)

AFNetworkReachabilityManager 是对 SystemConfiguration 模块的封装,苹果的文档中也有一个类似的项目 Reachability 这里对网络状态的监控跟苹果官方的实现几乎是完全相同的。


同样在 github 上有一个类似的项目叫做 Reachability 不过这个项目由于命名的原因可能会在审核时被拒绝


无论是 AFNetworkReachabilityManager,苹果官方的项目或者说 github 上的 Reachability,它们的实现都是类似的,而在这里我们会以 AFNetworking 中的 AFNetworkReachabilityManager 为例来说明在 iOS 开发中,我们是怎样监控网络状态的。

AFNetworkReachabilityManager 的使用和实现

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


  1. 初始化 AFNetworkReachabilityManager

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

  3. 设置 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;}
复制代码


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

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

  3. 设置一个默认的 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); } });}
复制代码


  1. 先调用 - stopMonitoring 方法,如果之前设置过对网络状态的监听,使用 SCNetworkReachabilityUnscheduleFromRunLoop 方法取消之前在 Main Runloop 中的监听


   - (void)stopMonitoring {        if (!self.networkReachability) {            return;        }
SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); }
复制代码


  1. 创建一个在每次网络状态改变时的回调


   __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`
复制代码


  1. 创建一个 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); } }
复制代码


  1. 当目标的网络状态改变时,会调用传入的回调


   SCNetworkReachabilitySetCallback(        (__bridge SCNetworkReachabilityRef)networkReachability,        AFNetworkReachabilityCallback,        &context    );
复制代码


  1. 在 Main Runloop 中对应的模式开始监控网络状态


   SCNetworkReachabilityScheduleWithRunLoop(        (__bridge SCNetworkReachabilityRef)networkReachability,        CFRunLoopGetMain(),        kCFRunLoopCommonModes    );
复制代码


  1. 获取当前的网络状态,调用 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];    });}
复制代码


  1. 调用 AFNetworkReachabilityStatusForFlags 获取当前的网络可达性状态

  2. 在主线程中异步执行上面传入的 callback block(设置 self 的网络状态,调用 networkReachabilityStatusBlock

  3. 发送 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 文件,唯一一个引用到这个类的地方。


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

总结

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

  2. 它是 AFNetworking 中一个即插即用的模块


本文转载自 Draveness 技术博客。


原文链接:https://draveness.me/afnetworking4


2019-12-10 17:471698

评论

发布
暂无评论
发现更多内容

加密原理详解:对称式加密VS非对称式加密

Java架构师迁哥

Rust从0到1-代码组织-模块

rust modules 模块

4种语义分割数据集Cityscapes上SOTA方法总结

华为云开发者联盟

语义分割 OCR 数据集Cityscapes HRNet SegFix

真的香!Github一夜爆火被各大厂要求直接下架的面试题库也太全了

Java架构之路

Java 程序员 架构 面试 编程语言

LiteOS内核源码分析:动态内存之Bestfit分配算法

华为云开发者联盟

LiteOS Huawei LiteOS 动态内存 Bestfit 分配算法

开发知识 | 即时通讯是怎么做到的?

YonBuilder低代码开发平台

大前端 即时通讯 APP开发 小程序制作 开发技巧

Android组件化和插件化开发

寻找生命中的美好

android 组件化 插件化

破解class文件的第一步:深入理解JAVA Class文件

华为云开发者联盟

Java JVM 索引 class文件

万字精华:好好巩固你的Nginx知识体系

学Java关注我

Java 编程 架构 程序人生 计算机

走完线上 BUG 定位最后一公里

阿里巴巴中间件

可观测性 bug bug修复

合约跟单系统搭建,合约一键跟单app

如何从零开始学Python:(7)如何解决发布和上传代码过程中遇到的问题?

广之巅

Python 4月日更

2021年处置非法集资部际联席会议:密切关注打着区块链、虚拟货币等旗号的新型风险

CECBC

世界读书日,爱奇艺ers的技术产品书单

爱奇艺技术产品团队

读书

饿了么EMonitor演进史

阿里巴巴中间件

可观测性 饿了么 emonitor etrace

理性看待区块链+大宗商品

CECBC

区块链

推荐5个4K视频下载网站 (百万优质资源)

科技猫

网站 分享 视频 经验 资源分享

Golang Test

escray

学习 极客时间 Go 语言 4月日更

开发环境上云,打造五星级开发体验

CODING DevOps

Kubernetes 云原生 CODING Nocalhost

这份阿里P8技术专家整理的《一面到底》Java岗,GitHub已标星79k

Java架构之路

Java 程序员 架构 面试 编程语言

总是记不住java的IO流用法?用N个问题教你掌握java IO流

华为云开发者联盟

Java 字符串 IO流 字节输入流 字符流

自动源代码质量度量(ISO/IEC 5055)

Tom(⊙o⊙)

软件质量 静态分析

欢迎 ProForma 的制造商 ThinkTilt 加入 Atlassian 的大家庭!

Atlassian

敏捷 esm ITSM Atlssian JiraServiceManagement

暴涨暴跌的牛市,普通人怎么和平发育?

CECBC

区块链

阿里“秘密团队”整理出来的一份Java面试复盘手册!全面复盘在望

Java架构之路

Java 程序员 架构 面试 编程语言

马丁策略倍投软件开发,量化倍投系统

千人万面奇妙自见:爱奇艺短视频推荐技术中多兴趣召回技术的演变

爱奇艺技术产品团队

推荐 短视频 模型 召回

阿里巴巴的“双11”高并发秒杀终极版教程!(Java语言设计)

Java 编程 程序员 架构

全网最全 ECMAScript 攻略

清秋

JavaScript ecmascript 大前端 ES6 Ecma

插件化库VirtualAPK详解

寻找生命中的美好

android 插件化 VirtualAPK

Flink的状态一致性

大数据技术指南

flink 4月日更

AFNetworkReachabilityManager 监控网络状态(四)_语言 & 开发_Draveness_InfoQ精选文章