原由
问题的原由很简单,就是我在使用支付宝花呗功能是,由于网络慢的原因重复点击了我的账单和我的额度时,出现页面重复加载的情况。点击次数和加载次数是相同的(我点击了5次跳了5次),并且是可以稳定复现的。但是这个小bug,说实话,无足轻重,完全不影响使用,更不会误导用户。
大概是去年11月我是发现过微信的一个导致app卡死的bug,当时没有什么头绪,后来也就放弃思考没有记录,至今懊恼不已。所以这次本着发现问题解决问题不逃避的想法,还是把这个写下来。虽然很easy,但求不被拍砖。
原因
其实问题的原因很容易就猜到,是在花呗页面点击我的账单是,app请求数据,但是由于网络的原因,接受数据大概延迟了两秒。而在这期间我以为有什么问题,所以点击了五次。在接受数据时,点击事件由于没有检测的原因而执行了5次,于是页面跳转了5次。
其实这个问题我自己在开发过程中也是发现过好几次,比如在亚程旅游iOSapp的自由行城市选择中,由于数据量大且有多个接口,会使用户明显感觉到延迟,用户在animation动画出现前快速点击两次,会有出现重复跳转的情况。直到这会儿才总结这个问题,惭愧。以前我是加个bool值来判断是否执行,我觉得这是很简洁明了的。但是如果有同样的情况又将要写重复的代码,所以将这个无论多少次点击跳转页面且执行一次的功能写成category。
解决方案
方案一外部阻止处理点击事件
通过bool值来判断是否执行过。代码量比较少就贴下了。
@interface UIView (delay)
@property (nonatomic, copy) dispatch_block_t oneTapBlock;
@property (nonatomic, assign) BOOL taskShouldBeCanceled;
@end
@implementation UIView (delay)
- (void)setOneTapBlock:(dispatch_block_t)oneTapBlock{
oneTapBlock();
//**细节在这里
self.taskShouldBeCanceled = YES;
}
- (dispatch_block_t)oneTapBlock{
return self.oneTapBlock;
}
- (void)setTaskShouldBeCanceled:(BOOL)taskShouldBeCanceled{
//**细节在这里
objc_setAssociatedObject(self, @"taskShouldBeCanceled", @(!taskShouldBeCanceled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)taskShouldBeCanceled{
return [objc_getAssociatedObject(self, @"taskShouldBeCanceled") boolValue];
}
@end
#import "BaseViewController.h"
#import "UIView+delay.h"
@interface BaseViewController ()
@property (nonatomic, strong) UIButton *button;
@end
@implementation BaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
_button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
_button.backgroundColor = [UIColor redColor];
[self.view addSubview:_button];
[_button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
}
- (void)click {
__weak typeof(self) weakSelf = self;
if (!_button.taskShouldBeCanceled) {
_button.oneTapBlock = ^{
//只需要将点击事件代码写在这里就可以
UIViewController *vc = [[UIViewController alloc] init];
vc.title = [NSString stringWithFormat:@"%@", @(arc4random() % 10)];
[weakSelf.navigationController pushViewController:vc animated:YES];
};
}
}
@end
方案二内部阻止处理点击事件
这个感觉比较困难,因为要在block内部检测自己是否已经被执行过,在跳转页面的这种情况可以通过当前viewcontroller来决定执行block内的点击事件。但是如果要在block内部来取消自己,感觉这是比较困难的,如果各位大神有什么好的想法恳求不吝赐教。
引申支付宝单车扫描
在使用的过程中我感觉,支付宝的扫描速度是要略微比微信慢的。一般微信点击后感觉都没有开始扫描就已经识别二维码,然后请求数据了。而支付宝感觉开始扫描了一秒或者0.5秒这样才识别二维码,然后请求数据了。这种毫秒级别的扫描,在平时可能没有什么太大的影响,但是在每天早上骑车上班的时候,确实被我放大了很多倍。另外在高频次的日常扫描支付的时候,我想各位看官也是能够体会到这种差别的。不得不承认微信的扫描优化独步江湖。
另外在使用单车的时候,其实我想问问为什么没有暂停功能。每次回家的时候会路过一些便利店或者是想要吃饭,但是如果把车放在外面不关闭,等我出来的时候车就被人骑走了。骑走了倒也无所谓,你倒是不骑了就给我关了啊。结果我在第二天早上,扫描时说我已经骑了八百多分钟,好像要十几块大洋,欲哭无泪。但是我在联系客服时,说明异常后没有扣款,这一点我是比较欣慰的。说到这个,想起了我一个哥们儿,也是类似的情况,但是骑的是mobike结果更是扣了几十块(心疼一秒钟)。其实单车可以更加人性化的,比如暂停功能,每天消费上限等等。
总之,一个服务更厉害,一个产品更厉害。
我的建议
我有什么建议?当然他们可以认同我提出的bug,并认同我的解决方案啊。
github源码
取消用户点击事件跨平台操作方案
由于有两位观众老爷的热烈讨论,给了我一点启发。
无论是native还是hybrid在前一页面请求数据后跳转到下一页面,如果没有做优化,在网络不佳的情况下就容易出现本文的问题。
1.如果是H5,可以利用定时器让函数延迟执行100ms,在100ms内如果还出发了函数可以删除上一次调用。最关键的就是这一点上了,js里面是可以取消函数调用的。
2.ios端,解决方案有很多
2.1在用户点击后500ms内不允许点击,当点击事件执行时恢复点击(外部控制)。
2.2如本文设置bool值来阻止点击事件执行(外部控制)。
2.3如果是写在block里面,而block是无法直接取消的,如果想要在内部取消可能需要建立检测(这一点我现在不知道如何下手,有大神指教木有),这样做比较麻烦。
3.android端,解决方案类似。
(注:最好是将这一功能抽出来,免得到处都是重复的代码。)
网友评论