AFNetworking 3.0 源碼解讀(九)之 AFNetworkActivityIndicatorManager


讓我們的APP像藝術品一樣優雅,開發工程師更像是一名匠人,不僅需要精湛的技藝,而且要有一顆匠心。

前言

AFNetworkActivityIndicatorManager 是對狀態欄中網絡激活那個小控件的管理。在平時的開發中,我們很可能忽略了它的存在。然而,實現對它的管理,讓我們的APP更符合人機交互,不也是件大快人心的事兒嗎。看下邊這張圖片就明白了:

AFNetworkActivityIndicatorManager 接口

// 這個宏的意思指下邊的類不能被擴展
NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.")

我們還是先看看,暴露出來的接口中,我們能夠做哪些事情。不得不說的是,AFNetworkActivityIndicatorManager 大部分功能是通過重寫setter方法實現的。

  • BOOL enabled 是否開啟? 默認是不開啟的。如果你的APP中使用了AFNetworking這個框架的話,只需要在 AppDelegateapplication: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM