讓我們的APP像藝術(shù)品一樣優(yōu)雅,開發(fā)工程師更像是一名匠人,不僅需要精湛的技藝,而且要有一顆匠心。
前言
AFNetworkActivityIndicatorManager 是對(duì)狀態(tài)欄中網(wǎng)絡(luò)激活那個(gè)小控件的管理。在平時(shí)的開發(fā)中,我們很可能忽略了它的存在。然而,實(shí)現(xiàn)對(duì)它的管理,讓我們的APP更符合人機(jī)交互,不也是件大快人心的事兒?jiǎn)帷?聪逻呥@張圖片就明白了:
AFNetworkActivityIndicatorManager 接口
// 這個(gè)宏的意思指下邊的類不能被擴(kuò)展
NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.")
我們還是先看看,暴露出來(lái)的接口中,我們能夠做哪些事情。不得不說的是,AFNetworkActivityIndicatorManager 大部分功能是通過重寫setter方法實(shí)現(xiàn)的。
-
BOOL enabled
是否開啟? 默認(rèn)是不開啟的。如果你的APP中使用了AFNetworking這個(gè)框架的話,只需要在AppDelegate
的application:didFinishLaunchingWithOptions:
方法中加入下邊這行代碼就行了:[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
-
BOOL networkActivityIndicatorVisible
這個(gè)屬性用來(lái)獲取和設(shè)置激活狀態(tài)。這個(gè)屬性支持kvo。如果是設(shè)置,首先回調(diào)用自己實(shí)現(xiàn)的控制轉(zhuǎn)態(tài)的block,如果沒有實(shí)現(xiàn)這個(gè)block,就直接通過UIApplication來(lái)設(shè)置激活狀態(tài)了。 -
NSTimeInterval activationDelay
激活延時(shí),指的是當(dāng)網(wǎng)絡(luò)開始到顯示激活的一個(gè)時(shí)間間隔。默認(rèn)的是1秒,為什么要設(shè)置這個(gè)呢?根據(jù)人機(jī)交互指南,有些網(wǎng)絡(luò)很快,這個(gè)情況就不需要顯示激活的那個(gè)狀態(tài)了。 -
NSTimeInterval completionDelay
狀態(tài)消失的延時(shí),默認(rèn)為0.17秒。 -
sharedManager
全局的單例對(duì)象。 -
(void)incrementActivityCount
增加激活的請(qǐng)求的數(shù)量,當(dāng)數(shù)量大于0,就處于激活狀態(tài)。 -
(void)decrementActivityCount
減少數(shù)量。 -
setNetworkingActivityActionWithBlock:
根據(jù)狀態(tài)來(lái)自定義事件。
AFNetworkActivityManagerState
激活一共分為四種狀態(tài):
typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) {
AFNetworkActivityManagerStateNotActive, // 未激活
AFNetworkActivityManagerStateDelayingStart, //激活前的延時(shí)階段
AFNetworkActivityManagerStateActive, // 激活
AFNetworkActivityManagerStateDelayingEnd // 取消階段
};
私有方法
static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0;
static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17;
// 獲取通知中的請(qǐng)求
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實(shí)現(xiàn)部分
由于內(nèi)部的實(shí)現(xiàn)比較簡(jiǎn)單,沒有特別難以理解的地方,在此就直接貼出代碼了:
@interface AFNetworkActivityIndicatorManager ()
//激活數(shù)
@property (readwrite, nonatomic, assign) NSInteger activityCount;
//激活前延時(shí)的定時(shí)器
@property (readwrite, nonatomic, strong) NSTimer *activationDelayTimer;
//失效后延時(shí)的定時(shí)器
@property (readwrite, nonatomic, strong) NSTimer *completionDelayTimer;
//是否是激活中
@property (readonly, nonatomic, getter = isNetworkActivityOccurring) BOOL networkActivityOccurring;
//激活事件的自定義屬性
@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock;
//當(dāng)前的狀態(tài)
@property (nonatomic, assign) AFNetworkActivityManagerState currentState;
@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
// 當(dāng)激活狀態(tài)改變后更新當(dāng)前的狀態(tài)
- (void)updateCurrentStateForNetworkActivityChange;
@end
--
+ (instancetype)sharedManager {
static AFNetworkActivityIndicatorManager *_sharedManager = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
不過,這里要說明一點(diǎn),激活與否的依據(jù)來(lái)源于AFNetworking中下邊的3個(gè)通知:
- 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()鎖的補(bǔ)充</font>
synchronized是一種鎖,這種鎖不管是在oc中還是java中用的都挺多的,而且這種鎖鎖得是對(duì)象。具體原理,可以看這篇文章后邊的 參考 那一部分。
總結(jié)一下,鎖一般用于多線程環(huán)境下對(duì)數(shù)據(jù)的操作中。在 AFNetworking 中我們見到了3種不同的鎖,分別是:
-
NSLock
-
dispatch_semaphore_wait
-
@synchronized
// enabled setter方法
- (void)setEnabled:(BOOL)enabled {
_enabled = enabled;
if (enabled == NO) {
//設(shè)置當(dāng)前狀態(tài)為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"];
// 這個(gè)方法可能會(huì)在多線程被調(diào)用多次,所以要加鎖
@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;
}
// 這個(gè)方法會(huì)涉及到界面的更新,因此要在主線程
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];
}
總結(jié)
說一下整個(gè)流程吧:
- 當(dāng)收到 AFNetworking 的AFNetworkingTaskDidResumeNotification通知后,調(diào)用
incrementActivityCount
方法。 - 在
incrementActivityCount
方法中把激活數(shù)+1,然后調(diào)用updateCurrentStateForNetworkActivityChange
方法更新當(dāng)前的狀態(tài)。 - 在
updateCurrentStateForNetworkActivityChange
方法中會(huì)設(shè)置當(dāng)前的狀態(tài),也就是調(diào)用setCurrentState:
方法。 - 在
setCurrentState:
方法中通過當(dāng)前的狀態(tài),來(lái)開啟或者關(guān)閉定時(shí)器,然后調(diào)用setNetworkActivityIndicatorVisible:
方法。 - 在
setNetworkActivityIndicatorVisible:
方法中設(shè)置激活狀態(tài)。
我在想,如果寫一個(gè)網(wǎng)絡(luò)框架,應(yīng)該是架構(gòu)在 AFNetworking 上,應(yīng)該在調(diào)用的時(shí)候,隱藏所有它的行跡,包括本片文章的這個(gè)功能。
參考
synchronized原理和鎖優(yōu)化
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