AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager

让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心。

前言

AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控件的管理。在平时的开发中,我们很可能忽略了它的存在。然而,实现对它的管理,让我们的APP更符合人机交互,不也是件大快人心的事儿吗。看下边这张图片就明白了:
AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager_第1张图片

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个通知:

  1. AFNetworkingTaskDidResumeNotification
  2. AFNetworkingTaskDidSuspendNotification
  3. 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];
}

@synchronized()锁的补充

synchronized是一种锁,这种锁不管是在oc中还是java中用的都挺多的,而且这种锁锁得是对象。具体原理,可以看这篇文章后边的 参考 那一部分。
总结一下,锁一般用于多线程环境下对数据的操作中。在 AFNetworking 中我们见到了3种不同的锁,分别是:

  1. NSLock 
  2. dispatch_semaphore_wait 
  3. @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];
}

总结

说一下整个流程吧:

  1. 当收到 AFNetworking 的AFNetworkingTaskDidResumeNotification通知后,调用incrementActivityCount方法。
  2. incrementActivityCount方法中把激活数+1,然后调用updateCurrentStateForNetworkActivityChange方法更新当前的状态。
  3. updateCurrentStateForNetworkActivityChange方法中会设置当前的状态,也就是调用setCurrentState:方法。
  4. setCurrentState:方法中通过当前的状态,来开启或者关闭定时器,然后调用setNetworkActivityIndicatorVisible:方法。
  5. 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

AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache

AFNetworking 3.0 源码解读(八)之 AFImageDownloader

你可能感兴趣的:(iOS-OC)