forked from draveness/analyze
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAFNetworkReachabilityManager 监控网络状态(四).md
311 lines (226 loc) · 13.6 KB
/
AFNetworkReachabilityManager 监控网络状态(四).md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# AFNetworkReachabilityManager 监控网络状态(四)
Blog: [Draveness](http://draveness.me)
<iframe src="http://ghbtns.com/github-btn.html?user=draveness&type=follow&size=large" height="30" width="240" frameborder="0" scrolling="0" style="width:240px; height: 30px;" allowTransparency="true"></iframe>
`AFNetworkReachabilityManager` 是对 `SystemConfiguration` 模块的封装,苹果的文档中也有一个类似的项目 [Reachability](https://developer.apple.com/library/ios/samplecode/reachability/) 这里对网络状态的监控跟苹果官方的实现几乎是完全相同的。
同样在 github 上有一个类似的项目叫做 [Reachability](https://github.com/tonymillion/Reachability) 不过这个项目**由于命名的原因可能会在审核时被拒绝**。
无论是 `AFNetworkReachabilityManager`,苹果官方的项目或者说 github 上的 Reachability,它们的实现都是类似的,而在这里我们会以 `AFNetworking` 中的 `AFNetworkReachabilityManager` 为例来说明在 iOS 开发中,我们是怎样监控网络状态的。
## AFNetworkReachabilityManager 的使用和实现
`AFNetworkReachabilityManager` 的使用还是非常简单的,只需要三个步骤,就基本可以完成对网络状态的监控。
1. [初始化 `AFNetworkReachabilityManager`](#init)
2. [调用 `startMonitoring` 方法开始对网络状态进行监控](#monitor)
3. [设置 `networkReachabilityStatusBlock` 在每次网络状态改变时, 调用这个 block](#block)
### <a id="init"></a>初始化 AFNetworkReachabilityManager
在初始化方法中,使用 `SCNetworkReachabilityCreateWithAddress` 或者 `SCNetworkReachabilityCreateWithName` 生成一个 `SCNetworkReachabilityRef` 的引用。
```objectivec
+ (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`
```objectivec
- (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 对象。
### <a id="monitor"></a>监控网络状态
在初始化 `AFNetworkReachabilityManager` 后,会调用 `startMonitoring` 方法开始监控网络状态。
```objectivec
- (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);
}
2. 创建一个在每次网络状态改变时的回调
__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`
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 对象
+ 这里的 `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);
}
}
4. 当目标的网络状态改变时,会调用传入的回调
SCNetworkReachabilitySetCallback(
(__bridge SCNetworkReachabilityRef)networkReachability,
AFNetworkReachabilityCallback,
&context
);
5. 在 Main Runloop 中对应的模式开始监控网络状态
SCNetworkReachabilityScheduleWithRunLoop(
(__bridge SCNetworkReachabilityRef)networkReachability,
CFRunLoopGetMain(),
kCFRunLoopCommonModes
);
6. 获取当前的网络状态,调用 callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
在下一节中会介绍上面所提到的一些 C 函数以及各种回调。
### <a id="block"></a>设置 networkReachabilityStatusBlock 以及回调
在 Main Runloop 中对网络状态进行监控之后,在每次网络状态改变,就会调用 `AFNetworkReachabilityCallback` 函数:
```objectivec
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
```
这里会从 `info` 中取出之前存在 `context` 中的 `AFNetworkReachabilityStatusBlock`。
```objectivec
__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` 函数:
```objectivec
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` 通知.
```objectivec
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`。
```objectivec
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` 的实例。
```objectivec
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
```
这是整个框架中除了 `AFNetworkReachabilityManager.h/m` 文件,**唯一一个**引用到这个类的地方。
在实际的使用中,我们也可以直接操作 `AFURLSessionManager` 的 `reachabilityManager` 来获取当前的网络可达性状态,而不是自己手动初始化一个实例,当然这么做也是没有任何问题的。
## 总结
1. `AFNetworkReachabilityManager` 实际上只是一个对底层 `SystemConfiguration` 库中的 C 函数封装的类,它为我们隐藏了 C 语言的实现,提供了统一的 Objective-C 语言接口
2. 它是 `AFNetworking` 中一个即插即用的模块
## 相关文章
关于其他 AFNetworking 源代码分析的其他文章:
+ [AFNetworking 概述(一)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/AFNetworking%20概述(一).md)
+ [AFNetworking 的核心 AFURLSessionManager(二)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/AFNetworking%20的核心%20AFURLSessionManager(二).md)
+ [处理请求和响应 AFURLSerialization(三)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/处理请求和响应%20AFURLSerialization(三).md)
+ [AFNetworkReachabilityManager 监控网络状态(四)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/AFNetworkReachabilityManager%20监控网络状态(四).md)
+ [验证 HTTPS 请求的证书(五)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/contents/AFNetworking/验证%20HTTPS%20请求的证书(五).md)
Follow: [@Draveness](https://github.com/Draveness)