前端时间在做听书功能的时候,遇到了一个问题,Handler 移除 Runnable 无效。 当时的场景是这样的:
代码是Kotlin,在启动播放的时候,延迟800毫米使用Handler发送一个消息,收到消息后,展示loading。如果在800毫秒之内就已经开始播放了,则移除消息,不会展示loading。但是在实际操作过程中发现消息移除不生效,loading还是会展示。下面通过一个例子来探究其中的原因。
val handler: Handler = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
}
}
val showDialogRunnable = {
Log.i(TAG, "在runnable里面展示弹窗")
showLoading()
}
注释1处,定义发送的延迟消息,用来展示loading,注意这是一个lambda表达式,并没有显式的声明类型。
fun onClick(v: View) {
when (v.id) {
R.id.btn_send_msg -> {
Log.i(TAG, "onClick: 发送延迟消息")
handler.postDelayed(showDialogRunnable, 5000)
}
R.id.btn_remove_msg -> {
Log.i(TAG, "onClick: 移除消息")
handler.removeCallbacks(showDialogRunnable)
}
}
}
结果发现loading还是展示出来了。这是什么原因呢?我们看一下Kotlin反编译出来的java代码。
定义的 showDialogRunnable 对象,可以看到其类型是 Function0 类型,并不是Runnable。
@NotNull
private final Function0 showDialogRunnable = (Function0)(new Function0() {
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
Log.i("TestFuncActivity", "在runnable里面展示弹窗");
TestFuncActivity.this.showLoading();
}
});
注意: 这就是问题的根本所在,我们本来期望的定义的是 Runnable 类型,结果并不是。
public final void onClick(@NotNull View v) {
Intrinsics.checkNotNullParameter(v, "v");
Handler var10000;
Object var10001;
Object var2;
switch(v.getId()) {
case 1000028:
Log.i("TestFuncActivity", "onClick: 发送延迟消息");
var10000 = this.handler;
var10001 = this.showDialogRunnable;
if(var10001 != null) {
var2 = var10001;
var10001 = new TestFuncActivity$sam$java_lang_Runnable$0((Function0) var2);
}
var10000.postDelayed((Runnable) var10001, 5000 L);
break;
case 1000172:
Log.i("TestFuncActivity", "onClick: 移除消息");
var10000 = this.handler;
var10001 = this.showDialogRunnable;
if(var10001 != null) {
var2 = var10001;
var10001 = new TestFuncActivity$sam$java_lang_Runnable$0((Function0) var2);
}
var10000.removeCallbacks((Runnable) var10001);
}
}
注释2处,利用 showDialogRunnable 对象创建了一个新对象 一个 TestFuncActivity
s
a
m
sam
samjava_lang_Runnable$0 对象。
TestFuncActivity
s
a
m
sam
samjava_lang_Runnable$0 这个类是什么东西呢,其实是继承了 Runnable 的一个类。
final class TestFuncActivity$sam$java_lang_Runnable$0 implements Runnable {
private final Function0
function;
TestFuncActivity$sam$java_lang_Runnable$0(Function0 var1) {
this.function = var1;
}
public final void run() {
Intrinsics.checkNotNullExpressionValue(this.function.invoke(), "invoke(...)");
}
}
//注释4处,这里有重新 new 了一个 TestFuncActivity
s
a
m
sam
samjava_lang_Runnable$0 对象。
通过注释2处和注释4处,我们知道了 postDelay 的对象 和 removeCallbacks 的对象是两个不同的对象,所以移除不起作用。
解决方法 : 显式使用Runnable
val showDialogRunnable: Runnable = Runnable {
Log.i(TAG, "在runnable里面展示弹窗")
showLoading()
}
我们看下反编译出来的代码,这个时候就是一个 Runnable 对象。
@NotNull
private final Runnable showDialogRunnable = (Runnable)(new Runnable() {
public final void run() {
Log.i("TestFuncActivity", "在runnable里面展示弹窗");
TestFuncActivity.this.showLoading();
}
});
public final void onClick(@NotNull View v) {
Intrinsics.checkNotNullParameter(v, "v");
switch(v.getId()) {
case 1000028:
Log.i("TestFuncActivity", "onClick: 发送延迟消息");
this.handler.postDelayed(this.showDialogRunnable, 5000 L);
break;
case 1000172:
Log.i("TestFuncActivity", "onClick: 移除消息");
this.handler.removeCallbacks(this.showDialogRunnable);
}
}
注释1处, postDelayed showDialogRunnable。 注释2处, removeCallbacks showDialogRunnable。
现在 postDelayed 和 removeCallbacks 的就是同一个对象了,可以正常移除。
|