讓我們的APP像藝術(shù)品一樣優(yōu)雅,開發(fā)工程師更像是一名匠人,不僅需要精湛的技藝持钉,而且要有一顆匠心。
前言
AFNetworkActivityIndicatorManager 是對狀態(tài)欄中網(wǎng)絡(luò)激活那個小控件的管理篱昔。在平時的開發(fā)中每强,我們很可能忽略了它的存在。然而州刽,實現(xiàn)對它的管理空执,讓我們的APP更符合人機(jī)交互,不也是件大快人心的事兒嗎穗椅”姘恚看下邊這張圖片就明白了:
AFNetworkActivityIndicatorManager 接口
// 這個宏的意思指下邊的類不能被擴(kuò)展
NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.")
我們還是先看看,暴露出來的接口中匹表,我們能夠做哪些事情邢羔。不得不說的是,AFNetworkActivityIndicatorManager 大部分功能是通過重寫setter方法實現(xiàn)的桑孩。
-
BOOL enabled
是否開啟拜鹤? 默認(rèn)是不開啟的。如果你的APP中使用了AFNetworking這個框架的話流椒,只需要在AppDelegate
的application:didFinishLaunchingWithOptions:
方法中加入下邊這行代碼就行了:[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
-
BOOL networkActivityIndicatorVisible
這個屬性用來獲取和設(shè)置激活狀態(tài)敏簿。這個屬性支持kvo。如果是設(shè)置宣虾,首先回調(diào)用自己實現(xiàn)的控制轉(zhuǎn)態(tài)的block惯裕,如果沒有實現(xiàn)這個block,就直接通過UIApplication來設(shè)置激活狀態(tài)了绣硝。 -
NSTimeInterval activationDelay
激活延時蜻势,指的是當(dāng)網(wǎng)絡(luò)開始到顯示激活的一個時間間隔。默認(rèn)的是1秒鹉胖,為什么要設(shè)置這個呢握玛?根據(jù)人機(jī)交互指南,有些網(wǎng)絡(luò)很快甫菠,這個情況就不需要顯示激活的那個狀態(tài)了挠铲。 -
NSTimeInterval completionDelay
狀態(tài)消失的延時,默認(rèn)為0.17秒寂诱。 -
sharedManager
全局的單例對象拂苹。 -
(void)incrementActivityCount
增加激活的請求的數(shù)量,當(dāng)數(shù)量大于0痰洒,就處于激活狀態(tài)瓢棒。 -
(void)decrementActivityCount
減少數(shù)量浴韭。 -
setNetworkingActivityActionWithBlock:
根據(jù)狀態(tài)來自定義事件。
AFNetworkActivityManagerState
激活一共分為四種狀態(tài):
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實現(xiàn)部分
由于內(nèi)部的實現(xiàn)比較簡單脯宿,沒有特別難以理解的地方念颈,在此就直接貼出代碼了:
@interface AFNetworkActivityIndicatorManager ()
//激活數(shù)
@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;
//當(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;
}
不過,這里要說明一點嗅绰,激活與否的依據(jù)來源于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()鎖的補(bǔ)充</font>
synchronized是一種鎖舍肠,這種鎖不管是在oc中還是java中用的都挺多的,而且這種鎖鎖得是對象窘面。具體原理翠语,可以看這篇文章后邊的 參考 那一部分。
總結(jié)一下财边,鎖一般用于多線程環(huán)境下對數(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"];
// 這個方法可能會在多線程被調(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;
}
// 這個方法會涉及到界面的更新谍夭,因此要在主線程
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é)
說一下整個流程吧:
- 當(dāng)收到 AFNetworking 的AFNetworkingTaskDidResumeNotification通知后,調(diào)用
incrementActivityCount
方法憨募。 - 在
incrementActivityCount
方法中把激活數(shù)+1紧索,然后調(diào)用updateCurrentStateForNetworkActivityChange
方法更新當(dāng)前的狀態(tài)。 - 在
updateCurrentStateForNetworkActivityChange
方法中會設(shè)置當(dāng)前的狀態(tài)菜谣,也就是調(diào)用setCurrentState:
方法珠漂。 - 在
setCurrentState:
方法中通過當(dāng)前的狀態(tài),來開啟或者關(guān)閉定時器尾膊,然后調(diào)用setNetworkActivityIndicatorVisible:
方法媳危。 - 在
setNetworkActivityIndicatorVisible:
方法中設(shè)置激活狀態(tài)。
我在想冈敛,如果寫一個網(wǎng)絡(luò)框架待笑,應(yīng)該是架構(gòu)在 AFNetworking 上,應(yīng)該在調(diào)用的時候抓谴,隱藏所有它的行跡暮蹂,包括本片文章的這個功能。
參考
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