原始碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager

堯少羽發表於2018-03-14

該文章閱讀的AFNetworking的版本為3.2.0。

這個類就是控制在網路請求時在狀態列左上角轉動的網路活動指示器的顯現與隱藏。

1.介面檔案

1.1.巨集

NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.")
複製程式碼

一上來我們可以看到有這樣一個巨集,經過查閱,這個巨集的意思是,這個類在擴充套件中不可用。這個擴充套件是iOS8的新特性,其他app可以與擴充套件進行資料交換。

1.2.屬性

/**
 網路活動指示器是否啟用
 */
@property (nonatomic, assign, getter = isEnabled) BOOL enabled;

/**
 網路活動指示器是否顯示
*/
@property (readonly, nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;

/**
 網路活動指示器顯示延時,預設是網路請求開始後1秒
 */
@property (nonatomic, assign) NSTimeInterval activationDelay;

/**
 網路活動指示器隱藏延時,預設是網路請求結束後0.17秒
 */

@property (nonatomic, assign) NSTimeInterval completionDelay;
複製程式碼

1.3.方法

/**
 獲取網路活動指示器單例
 */
+ (instancetype)sharedManager;

/**
 增加活動的網路請求的數量
 */
- (void)incrementActivityCount;

/**
 減少活動的網路請求的數量
 */
- (void)decrementActivityCount;

/**
 設定網路活動指示器隱藏和顯示時的自定義事件
 */
- (void)setNetworkingActivityActionWithBlock:(nullable void (^)(BOOL networkActivityIndicatorVisible))block;
複製程式碼

2.實現檔案

2.1.列舉

typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) {
    AFNetworkActivityManagerStateNotActive,
    AFNetworkActivityManagerStateDelayingStart,
    AFNetworkActivityManagerStateActive,
    AFNetworkActivityManagerStateDelayingEnd
};
複製程式碼

這個列舉定義了網路活動指示器的狀態:

AFNetworkActivityManagerStateNotActive表示網路活動指示器處於非活動狀態 AFNetworkActivityManagerStateDelayingStart表示網路活動指示器處於延時開始狀態 AFNetworkActivityManagerStateActive表示網路活動指示器處於活動狀態 AFNetworkActivityManagerStateDelayingEnd表示網路活動指示器處於延時結束狀態

2.2.靜態常量

/**
 定義了開始延時時間,為1秒
 */
static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0;

/**
 定義了結束延時時間,為0.17秒
 */
static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17;
複製程式碼

2.3.靜態方法

/**
 這個方法使用者獲取通知中的網路請求物件
 */
static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) {
    if ([[notification object] respondsToSelector:@selector(originalRequest)]) {
        return [(NSURLSessionTask *)[notification object] originalRequest];
    } else {
        return nil;
    }
}
複製程式碼

2.4.別名

/**
 定義了網路狀態發生變化時的回撥block
 */
typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisible);
複製程式碼

2.5.類擴充套件

2.5.1.屬性

/**
 活動請求數量
 */
@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;

/**
 網路狀態發生變化時的回撥block
 */
@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock;

/**
 當前狀態
 */
@property (nonatomic, assign) AFNetworkActivityManagerState currentState;

/**
 網路活動指示器是否顯示
 */
@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
複製程式碼

2.5.2.方法

/**
 根據當前的狀態改變網路活動指示器的狀態
 */
- (void)updateCurrentStateForNetworkActivityChange;
複製程式碼

2.6.方法實現

  • 生命週期方法
+ (instancetype)sharedManager {
    static AFNetworkActivityIndicatorManager *_sharedManager = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _sharedManager = [[self alloc] init];
    });

    return _sharedManager;
}

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    // 記錄當前狀態是非活動狀態
    self.currentState = AFNetworkActivityManagerStateNotActive;
    // 監聽了AFURLSessionManager的三個通知,分別是任務已經開始、任務已經暫停和任務已經結束
    [[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];
}
複製程式碼
  • 公共方法
- (void)setEnabled:(BOOL)enabled {
    _enabled = enabled;
    // 如果設定為NO,就把網路活動指示器的狀態設定為非活動狀態
    if (enabled == NO) {
        [self setCurrentState:AFNetworkActivityManagerStateNotActive];
    }
}

- (void)setNetworkingActivityActionWithBlock:(void (^)(BOOL networkActivityIndicatorVisible))block {
    // 記錄傳入的block
    self.networkActivityActionBlock = block;
}

- (BOOL)isNetworkActivityOccurring {
    // 加鎖獲取網路請求數量,大於0就是正在活動狀態
    @synchronized(self) {
        return self.activityCount > 0;
    }
}

- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible {
    // 如果新老資料不一致
    if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) {
        // 手動實現networkActivityIndicatorVisible屬性的KVO方法
        [self willChangeValueForKey:@"networkActivityIndicatorVisible"];
        // 加鎖賦值
        @synchronized(self) {
             _networkActivityIndicatorVisible = networkActivityIndicatorVisible;
        }
        [self didChangeValueForKey:@"networkActivityIndicatorVisible"];
        if (self.networkActivityActionBlock) {
            // 如果設定了回撥block就呼叫
            self.networkActivityActionBlock(networkActivityIndicatorVisible);
        } else {
            // 如果沒有設定回撥block就直接設定網路活動指示器的顯示狀態
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible];
        }
    }
}

- (void)setActivityCount:(NSInteger)activityCount {
    // 加鎖賦值
	@synchronized(self) {
		_activityCount = activityCount;
	}

    // 主佇列非同步呼叫
    dispatch_async(dispatch_get_main_queue(), ^{
        // 更新當前網路狀態
        [self updateCurrentStateForNetworkActivityChange];
    });
}

- (void)incrementActivityCount {
    // 手動實現activityCount屬性的KVO方法
    [self willChangeValueForKey:@"activityCount"];
    // 加鎖賦值
	@synchronized(self) {
		_activityCount++;
	}
    [self didChangeValueForKey:@"activityCount"];
    
    // 主佇列非同步呼叫更新當前網路狀態
    dispatch_async(dispatch_get_main_queue(), ^{
        [self updateCurrentStateForNetworkActivityChange];
    });
}

- (void)decrementActivityCount {
    // 手動實現activityCount屬性的KVO方法
    [self willChangeValueForKey:@"activityCount"];
    // 加鎖賦值
	@synchronized(self) {
	    // 不能小於零
		_activityCount = MAX(_activityCount - 1, 0);
	}
    [self didChangeValueForKey:@"activityCount"];

    // 主佇列非同步呼叫更新當前網路狀態
    dispatch_async(dispatch_get_main_queue(), ^{
        [self updateCurrentStateForNetworkActivityChange];
    });
}
複製程式碼
  • 私有方法
- (void)networkRequestDidStart:(NSNotification *)notification {
    // 接收到任務開始的通知如果請求物件中有URL就增加請求活動數量
    if ([AFNetworkRequestFromNotification(notification) URL]) {
        [self incrementActivityCount];
    }
}

- (void)networkRequestDidFinish:(NSNotification *)notification {
    // 接收到任務結束的通知如果請求物件中有URL就減少請求活動數量
    if ([AFNetworkRequestFromNotification(notification) URL]) {
        [self decrementActivityCount];
    }
}

- (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
    // 加鎖保護
    @synchronized(self) {
        // 如果新老資料不一致
        if (_currentState != currentState) {
            // 手動實現currentState屬性的KVO方法
            [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];
}
複製程式碼

3.總結

看完了程式碼,我們可以梳理一下AFNetworkActivityIndicatorManager類的工作流程:

  • 顯示網路活動指示器

1.通過在初始化方法中註冊通知監聽AFNetworkingtask的開始、暫停和結束。

2.當接收到task開始的通知時,就會在通知的回撥方法中記錄當前網路請求活動數量,手動傳送KOV,然後呼叫更新狀態方法。

3.在更新狀態方法中,如果當前的狀態是非活動,並且有網路請求活動,就會將當前狀態設定為延時開始狀態。

4.在currentState屬性的setter中,先手動傳送KVO,如果發現設定的狀態為開始延時狀態,就會開啟開始延時計時器。

5.在1秒過後就會觸發計時器方法,在計時器方法中,如果發現當前依然有網路請求在進行中,就將當前狀態設定為活動狀態。但是如果當前已經沒有進行中的網路請求了,就會把狀態設定為非活動狀態。

6.這時就又回到了重寫的currentState屬性的setter中,依舊是先手動傳送KVO,如果發現設定的狀態為開始狀態,就結束掉完成延時計時器,並顯示網路活動指示器。

  • 隱藏網路活動指示器

7.當接收到task結束的通知時,同樣的會在通知的回撥方法中記錄當前網路請求活動數量,手動傳送KOV,然後呼叫更新狀態方法。

8.在更新狀態方法中,如果當前的狀態是活動,並且已經沒有進行中的網路請求了,就會將當前狀態設定為延時結束狀態。

9.在重寫的currentState屬性setter中,除了手動傳送KVO,如果發現設定的狀態為延時結束狀態,就會開啟完成延時計時器。

10.在0.17秒過後,觸發計時器方法,計時器方法中會將狀態設定為非活動狀態,在currentState的setter中,先手動傳送KVO,在非活動狀態下就會結束掉開始和完成延時計時器,並隱藏網路活動指示器

原始碼閱讀系列:AFNetworking

原始碼閱讀:AFNetworking(一)——從使用入手

原始碼閱讀:AFNetworking(二)——AFURLRequestSerialization

原始碼閱讀:AFNetworking(三)——AFURLResponseSerialization

原始碼閱讀:AFNetworking(四)——AFSecurityPolicy

原始碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager

原始碼閱讀:AFNetworking(六)——AFURLSessionManager

原始碼閱讀:AFNetworking(七)——AFHTTPSessionManager

原始碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache

原始碼閱讀:AFNetworking(九)——AFImageDownloader

原始碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager

原始碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking

原始碼閱讀:AFNetworking(十二)——UIButton+AFNetworking

原始碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking

原始碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking

原始碼閱讀:AFNetworking(十五)——UIRefreshControl+AFNetworking

原始碼閱讀:AFNetworking(十六)——UIWebView+AFNetworking

相關文章