讓我們的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];
}
@synchronized()鎖的補充
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
