首页 技术 正文
技术 2022年11月7日
0 收藏 553 点赞 958 浏览 9472 个字

让我们的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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,951
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,477
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,290
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,107
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,739
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,773