让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心。
前言
AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控件的管理。在平时的开发中,我们很可能忽略了它的存在。然而,实现对它的管理,让我们的APP更符合人机交互,不也是件大快人心的事儿吗。看下边这张图片就明白了:
AFNetworkActivityIndicatorManager 接口
// 这个宏的意思指下边的类不能被扩展
NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.")
我们还是先看看,暴露出来的接口中,我们能够做哪些事情。不得不说的是,AFNetworkActivityIndicatorManager 大部分功能是通过重写setter方法实现的。
-
BOOL enabled
是否开启? 默认是不开启的。如果你的APP中使用了AFNetworking这个框架的话,只需要在AppDelegate
的application:didFinishLaunchingWithOptions:
方法中加入下边这行代码就行了:[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
-
BOOL networkActivityIndicatorVisible
这个属性用来获取和设置激活状态。这个属性支持kvo。如果是设置,首先回调用自己实现的控制转态的block,如果没有实现这个block,就直接通过UIApplication来设置激活状态了。 -
NSTimeInterval activationDelay
激活延时,指的是当网络开始到显示激活的一个时间间隔。默认的是1秒,为什么要设置这个呢?根据人机交互指南,有些网络很快,这个情况就不需要显示激活的那个状态了。 -
NSTimeInterval completionDelay
状态消失的延时,默认为0.17秒。 -
sharedManager
全局的单例对象。 -
(void)incrementActivityCount
增加激活的请求的数量,当数量大于0,就处于激活状态。 -
(void)decrementActivityCount
减少数量。 -
setNetworkingActivityActionWithBlock:
根据状态来自定义事件。
AFNetworkActivityManagerState
激活一共分为四种状态:
typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) {
AFNetworkActivityManagerStateNotActive, // 未激活
AFNetworkActivityManagerStateDelayingStart, //激活前的延时阶段
AFNetworkActivityManagerStateActive, // 激活
AFNetworkActivityManagerStateDelayingEnd // 取消阶段
};
私有方法
static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0;
static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17;
// 获取通知中的请求
static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) {
if ([[notification object] respondsToSelector:@selector(originalRequest)]) {
return [(NSURLSessionTask *)[notification object] originalRequest];
} else {
return nil;
}
}
typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisible);
AFNetworkActivityIndicatorManager实现部分
由于内部的实现比较简单,没有特别难以理解的地方,在此就直接贴出代码了:
@interface AFNetworkActivityIndicatorManager ()
//激活数
@property (readwrite, nonatomic, assign) NSInteger activityCount;
//激活前延时的定时器
@property (readwrite, nonatomic, strong) NSTimer *activationDelayTimer;
//失效后延时的定时器
@property (readwrite, nonatomic, strong) NSTimer *completionDelayTimer;
//是否是激活中
@property (readonly, nonatomic, getter = isNetworkActivityOccurring) BOOL networkActivityOccurring;
//激活事件的自定义属性
@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock;
//当前的状态
@property (nonatomic, assign) AFNetworkActivityManagerState currentState;
@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
// 当激活状态改变后更新当前的状态
- (void)updateCurrentStateForNetworkActivityChange;
@end
--
+ (instancetype)sharedManager {
static AFNetworkActivityIndicatorManager *_sharedManager = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
不过,这里要说明一点,激活与否的依据来源于AFNetworking中下边的3个通知:
- AFNetworkingTaskDidResumeNotification
- AFNetworkingTaskDidSuspendNotification
- AFNetworkingTaskDidCompleteNotification
--
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.currentState = AFNetworkActivityManagerStateNotActive;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil];
self.activationDelay = kDefaultAFNetworkActivityManagerActivationDelay;
self.completionDelay = kDefaultAFNetworkActivityManagerCompletionDelay;
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_activationDelayTimer invalidate];
[_completionDelayTimer invalidate];
}
<font color=orange>@synchronized()锁的补充</font>
synchronized是一种锁,这种锁不管是在oc中还是java中用的都挺多的,而且这种锁锁得是对象。具体原理,可以看这篇文章后边的 参考 那一部分。
总结一下,锁一般用于多线程环境下对数据的操作中。在 AFNetworking 中我们见到了3种不同的锁,分别是:
-
NSLock
-
dispatch_semaphore_wait
-
@synchronized
// enabled setter方法
- (void)setEnabled:(BOOL)enabled {
_enabled = enabled;
if (enabled == NO) {
//设置当前状态为not
[self setCurrentState:AFNetworkActivityManagerStateNotActive];
}
}
--
// 自定义block的setter
- (void)setNetworkingActivityActionWithBlock:(void (^)(BOOL networkActivityIndicatorVisible))block {
self.networkActivityActionBlock = block;
}
--
// isNetworkActivityOccurring的getter
- (BOOL)isNetworkActivityOccurring {
@synchronized(self) {
return self.activityCount > 0;
}
}
--
// networkActivityIndicatorVisible的setter
- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible {
if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) {
// 激活kvo
[self willChangeValueForKey:@"networkActivityIndicatorVisible"];
// 这个方法可能会在多线程被调用多次,所以要加锁
@synchronized(self) {
_networkActivityIndicatorVisible = networkActivityIndicatorVisible;
}
[self didChangeValueForKey:@"networkActivityIndicatorVisible"];
if (self.networkActivityActionBlock) {
self.networkActivityActionBlock(networkActivityIndicatorVisible);
} else {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible];
}
}
}
--
// activityCount的setter
- (void)setActivityCount:(NSInteger)activityCount {
@synchronized(self) {
_activityCount = activityCount;
}
// 这个方法会涉及到界面的更新,因此要在主线程
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
--
- (void)incrementActivityCount {
[self willChangeValueForKey:@"activityCount"];
@synchronized(self) {
_activityCount++;
}
[self didChangeValueForKey:@"activityCount"];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
--
- (void)decrementActivityCount {
[self willChangeValueForKey:@"activityCount"];
@synchronized(self) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
_activityCount = MAX(_activityCount - 1, 0);
#pragma clang diagnostic pop
}
[self didChangeValueForKey:@"activityCount"];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
--
//通知方法
- (void)networkRequestDidStart:(NSNotification *)notification {
if ([AFNetworkRequestFromNotification(notification) URL]) {
[self incrementActivityCount];
}
}
//通知方法
- (void)networkRequestDidFinish:(NSNotification *)notification {
if ([AFNetworkRequestFromNotification(notification) URL]) {
[self decrementActivityCount];
}
}
--
- (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
@synchronized(self) {
if (_currentState != currentState) {
[self willChangeValueForKey:@"currentState"];
_currentState = currentState;
switch (currentState) {
case AFNetworkActivityManagerStateNotActive:
[self cancelActivationDelayTimer];
[self cancelCompletionDelayTimer];
[self setNetworkActivityIndicatorVisible:NO];
break;
case AFNetworkActivityManagerStateDelayingStart:
[self startActivationDelayTimer];
break;
case AFNetworkActivityManagerStateActive:
[self cancelCompletionDelayTimer];
[self setNetworkActivityIndicatorVisible:YES];
break;
case AFNetworkActivityManagerStateDelayingEnd:
[self startCompletionDelayTimer];
break;
}
}
[self didChangeValueForKey:@"currentState"];
}
}
--
- (void)updateCurrentStateForNetworkActivityChange {
if (self.enabled) {
switch (self.currentState) {
case AFNetworkActivityManagerStateNotActive:
if (self.isNetworkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateDelayingStart];
}
break;
case AFNetworkActivityManagerStateDelayingStart:
//No op. Let the delay timer finish out.
break;
case AFNetworkActivityManagerStateActive:
if (!self.isNetworkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateDelayingEnd];
}
break;
case AFNetworkActivityManagerStateDelayingEnd:
if (self.isNetworkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateActive];
}
break;
}
}
}
--
- (void)startActivationDelayTimer {
self.activationDelayTimer = [NSTimer
timerWithTimeInterval:self.activationDelay target:self selector:@selector(activationDelayTimerFired) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.activationDelayTimer forMode:NSRunLoopCommonModes];
}
- (void)activationDelayTimerFired {
if (self.networkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateActive];
} else {
[self setCurrentState:AFNetworkActivityManagerStateNotActive];
}
}
- (void)startCompletionDelayTimer {
[self.completionDelayTimer invalidate];
self.completionDelayTimer = [NSTimer timerWithTimeInterval:self.completionDelay target:self selector:@selector(completionDelayTimerFired) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.completionDelayTimer forMode:NSRunLoopCommonModes];
}
- (void)completionDelayTimerFired {
[self setCurrentState:AFNetworkActivityManagerStateNotActive];
}
- (void)cancelActivationDelayTimer {
[self.activationDelayTimer invalidate];
}
- (void)cancelCompletionDelayTimer {
[self.completionDelayTimer invalidate];
}
总结
说一下整个流程吧:
- 当收到 AFNetworking 的AFNetworkingTaskDidResumeNotification通知后,调用
incrementActivityCount
方法。 - 在
incrementActivityCount
方法中把激活数+1,然后调用updateCurrentStateForNetworkActivityChange
方法更新当前的状态。 - 在
updateCurrentStateForNetworkActivityChange
方法中会设置当前的状态,也就是调用setCurrentState:
方法。 - 在
setCurrentState:
方法中通过当前的状态,来开启或者关闭定时器,然后调用setNetworkActivityIndicatorVisible:
方法。 - 在
setNetworkActivityIndicatorVisible:
方法中设置激活状态。
我在想,如果写一个网络框架,应该是架构在 AFNetworking 上,应该在调用的时候,隐藏所有它的行迹,包括本片文章的这个功能。
参考
synchronized原理和锁优化
objective-c @synchronized 锁用法
推荐阅读
AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager
AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy
AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization
AFNetworking 3.0 源码解读(五)之 AFURLSessionManager
AFNetworking 3.0 源码解读(六)之 AFHTTPSessionManager