AFNetworking 3.0 源碼解讀(九)之 AFNetworkActivityIndicatorManager

讓我們的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這個框架的話流椒,只需要在 AppDelegateapplication: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個通知:

  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];
}

<font color=orange>@synchronized()鎖的補(bǔ)充</font>

synchronized是一種鎖舍肠,這種鎖不管是在oc中還是java中用的都挺多的,而且這種鎖鎖得是對象窘面。具體原理翠语,可以看這篇文章后邊的 參考 那一部分。
總結(jié)一下财边,鎖一般用于多線程環(huán)境下對數(shù)據(jù)的操作中肌括。在 AFNetworking 中我們見到了3種不同的鎖,分別是:

  1. NSLock
  2. dispatch_semaphore_wait
  3. @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é)

說一下整個流程吧:

  1. 當(dāng)收到 AFNetworking 的AFNetworkingTaskDidResumeNotification通知后,調(diào)用incrementActivityCount方法憨募。
  2. incrementActivityCount方法中把激活數(shù)+1紧索,然后調(diào)用updateCurrentStateForNetworkActivityChange方法更新當(dāng)前的狀態(tài)。
  3. updateCurrentStateForNetworkActivityChange方法中會設(shè)置當(dāng)前的狀態(tài)菜谣,也就是調(diào)用setCurrentState:方法珠漂。
  4. setCurrentState:方法中通過當(dāng)前的狀態(tài),來開啟或者關(guān)閉定時器尾膊,然后調(diào)用setNetworkActivityIndicatorVisible:方法媳危。
  5. 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

AFNetworking 3.0 源碼解讀(七)之 AFAutoPurgingImageCache

AFNetworking 3.0 源碼解讀(八)之 AFImageDownloader

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末齐邦,一起剝皮案震驚了整個濱河市椎侠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌措拇,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慎宾,死亡現(xiàn)場離奇詭異丐吓,居然都是意外死亡浅悉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門券犁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來术健,“玉大人,你說我怎么就攤上這事粘衬≤窆溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵稚新,是天一觀的道長勘伺。 經(jīng)常有香客問我,道長褂删,這世上最難降的妖魔是什么飞醉? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮屯阀,結(jié)果婚禮上缅帘,老公的妹妹穿的比我還像新娘。我一直安慰自己难衰,他們只是感情好钦无,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盖袭,像睡著了一般失暂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苍凛,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天趣席,我揣著相機(jī)與錄音,去河邊找鬼醇蝴。 笑死宣肚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悠栓。 我是一名探鬼主播霉涨,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惭适!你這毒婦竟也來了笙瑟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤癞志,失蹤者是張志新(化名)和其女友劉穎往枷,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡错洁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年秉宿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屯碴。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡描睦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出导而,到底是詐尸還是另有隱情忱叭,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布今艺,位于F島的核電站韵丑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洼滚。R本人自食惡果不足惜埂息,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遥巴。 院中可真熱鬧千康,春花似錦、人聲如沸铲掐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摆霉。三九已至豪椿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間携栋,已是汗流浹背搭盾。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留婉支,地道東北人鸯隅。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像向挖,于是被迫代替她去往敵國和親蝌以。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容