有一天,测试吴找到我说:“蔡工,这里有你的bug,麻烦看一下”。然后在手机上操作了一通,是我正在开发的app中某个搜索页面,按钮点击偶尔会失灵的问题。
我按照他的步骤也跟着操作了一遍,并没有问题,说道:“我操作怎么没问题,你确定这里有bug?”。
“肯定是有问题的,我再操作一次给你看,先这样……,然后再这样……”。测试吴又操作了一遍,然而,这一次bug没有如期而至,他尴尬的看着我:“这个bug不是必现的,具有偶发性,但我确定这里是有问题的,我再试给你看”。
看着测试吴在手机上捣鼓了十几分钟还没有想走的意思,我从抽屉中,默默的抽出了一把40厘米的大刀,往桌上一放,面带微笑的说:“没事,我有时间,我等你……”
只见测试吴宛如闪电般,从我的视野中消失了。看着他离去的身影,我满意的点了点头,收起了桌子上的大刀。
这个场面是否如何熟悉?没错,这就是开发与测试的日常。当然,作为一名品格高尚的资深码农,我当然不会抽出40厘米的大刀,我会非常非常耐心的排查每一个bug……
回到正题,今日的bug有些棘手,要反复查看代码才能发现端倪。问题如下图,点击按钮1,再快速点击按钮2,就会出现按钮2无法点击的问题
按钮1和按钮2都是使用了一个叫AppButton的组件。在onPressed事件中,某师兄为了解决按钮重复点击问题,调用了Utils.throttle节流函数。从而引发了此次bug。
class AppButton extends ButtonStyleButton {
AppButton({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
FocusNode? focusNode,
bool autofocus = false,
Clip clipBehavior = Clip.none,
this.textStyle,
this.padding,
this.buttonStyle = AppButtonStyle.primary,
required String text,
}) : super(
key: key,
/// 解决按钮重复点击,调用了Utils.throttle节流函数
onPressed: Utils.throttle(onPressed!),
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: null,
focusNode: focusNode,
autofocus: autofocus,
clipBehavior: clipBehavior,
child: Text(
text,
),
),
);
/// ...
}
/// 功能工具类
class Utils {
static var enable = true;
///防止重复点击 规定时间内只触发一次
///func 要执行的方法
static Function() throttle(
Function func, {
Duration delay = const Duration(milliseconds: 1000),
}) {
return () {
if (enable) {
enable = false;
func();
Future.delayed(delay, () {
enable = true;
});
}
};
}
}
最终把问题锁定在Utils.throttle函数,因为按钮1和按钮2都调用了同一个函数,按钮1点击时,1s的时间内enable变量还处于false状态,按钮2就会无法执行func方法,要等1s过后才能再次点击。
所以最大的问题就是两个不相干的按钮,却因为同一个throttle函数引发了按钮1节流了按钮2。
知道了问题的原因,就能对症下药,解决的思路也很简单,就是判断多次点击是否来自同一个按钮,不同按钮当然就让它们不相互干扰。
/// 功能工具类
class Utils {
static var enable = true;
/// [bug修复]:
/// 问题:如果两个按钮A和B同时调用了throttle方法,它们就会相互影响,
/// 就会出现按钮A点击后[delay]时间内无法点击B,因为它们共用了[enable]。
///
/// 解决:增加[_hashCode]变量,将[func]的hashCode缓存起来,不同的func hashCode视为不同按钮,
/// 不同按钮重置enable,同个hashCode刚不受此规则影响。
///
/// 注:hashCode是什么?
/// hashCode是dart为每个对象生成的随机值,代表这个对象运行时的身份编码,具有唯一性。
static int? _hashCode;
///防止重复点击 规定时间内只触发一次
///func 要执行的方法
static Function() throttle(
Function func, {
Duration delay = const Duration(milliseconds: 1000),
}) {
return () {
if (_hashCode != func.hashCode) {
enable = true;
_hashCode = func.hashCode;
}
if (enable) {
enable = false;
func();
Future.delayed(delay, () {
enable = true;
});
}
};
}
}
解决完bug,测试吴向我竖起了大拇指,给了我一波赞,然后高高兴兴的离开了。看着他离去的背影,我默默的把抽屉关紧,心里暗想:“还好把问题解决了,不然就要晾出40厘米的大刀,才能把事情摆平……”。