Kotlin中 Handler 移除 Runnable 无效

前端时间在做听书功能的时候,遇到了一个问题,Handler 移除 Runnable 无效。 当时的场景是这样的:

代码是Kotlin,在启动播放的时候,延迟800毫米使用Handler发送一个消息,收到消息后,展示loading。如果在800毫秒之内就已经开始播放了,则移除消息,不会展示loading。但是在实际操作过程中发现消息移除不生效,loading还是会展示。下面通过一个例子来探究其中的原因。


//定义handler
val handler: Handler = object : Handler() {

    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
    }
}

//注释1处,定义发送的延迟消息,用来展示loading,注意这是一个lambda表达式,并没有显式的声明类型
val showDialogRunnable = {
    Log.i(TAG, "在runnable里面展示弹窗")
    showLoading()
}

注释1处,定义发送的延迟消息,用来展示loading,注意这是一个lambda表达式,并没有显式的声明类型。

fun onClick(v: View) {
    when (v.id) {
        R.id.btn_send_msg -> {
            //点击按钮后,延迟5秒发送消息展示loading
            Log.i(TAG, "onClick: 发送延迟消息")
            handler.postDelayed(showDialogRunnable, 5000)
        }
        R.id.btn_remove_msg -> {
            //在5秒之内移除消息,让loading展示不出来
            Log.i(TAG, "onClick: 移除消息")
            handler.removeCallbacks(showDialogRunnable)
        }
    }
}

结果发现loading还是展示出来了。这是什么原因呢?我们看一下Kotlin反编译出来的java代码。

定义的 showDialogRunnable 对象,可以看到其类型是 Function0 类型,并不是Runnable。

@NotNull
private final Function0 showDialogRunnable = (Function0)(new Function0() {
    // $FF: synthetic method
    // $FF: bridge method
    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;
            //注释1处,获取 showDialogRunnable 对象
            var10001 = this.showDialogRunnable;
            if(var10001 != null) {
                var2 = var10001;
                //注释2处,利用 showDialogRunnable 对象创建了一个新对象 一个 TestFuncActivity$sam$java_lang_Runnable$0 对象。
                var10001 = new TestFuncActivity$sam$java_lang_Runnable$0((Function0) var2);
            }
            //注释3处,将包装后的对象传递给Handler的 postDelayed 方法。
            var10000.postDelayed((Runnable) var10001, 5000 L);
            break;
        case 1000172:
            Log.i("TestFuncActivity", "onClick: 移除消息");
            var10000 = this.handler;
            var10001 = this.showDialogRunnable;
            if(var10001 != null) {
                var2 = var10001;
                //注释4处,这里有重新 new 了一个  TestFuncActivity$sam$java_lang_Runnable$0 对象。
                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 {
    // $FF: synthetic field
    private final Function0
    function;
    TestFuncActivity$sam$java_lang_Runnable$0(Function0 var1) {
            this.function = var1;
        }
        // $FF: synthetic method
    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: 发送延迟消息");
            //注释1处, postDelayed showDialogRunnable
            this.handler.postDelayed(this.showDialogRunnable, 5000 L);
            break;
        case 1000172:
            Log.i("TestFuncActivity", "onClick: 移除消息");
            //注释2处, removeCallbacks showDialogRunnable
            this.handler.removeCallbacks(this.showDialogRunnable);
    }
}

注释1处, postDelayed showDialogRunnable。
注释2处, removeCallbacks showDialogRunnable。

现在 postDelayed 和 removeCallbacks 的就是同一个对象了,可以正常移除。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin,使用Handler调用另一个Handler有两种方法: 方法1:使用post方法 您可以使用post方法将Runnable对象发送到目标Handler的消息队列,以便在稍后的时间执行。这可以通过以下方式完成: ```kotlin val handler1 = Handler(Looper.getMainLooper()) // 创建第一个Handler val handler2 = Handler() // 创建第二个Handler handler1.post(object : Runnable { override fun run() { // 在第一个Handler线程执行 handler2.post(object : Runnable { override fun run() { // 在第二个Handler线程执行 } }) } }) ``` 在这个例子,我们创建了两个Handler。在第一个Handler的线程,我们使用post方法将一个Runnable对象发送到第二个Handler的消息队列,以便在稍后的时间执行。 方法2:使用sendMessage方法 您还可以使用sendMessage方法将Message对象发送到目标Handler的消息队列,以便在稍后的时间执行。这可以通过以下方式完成: ```kotlin val handler1 = Handler(Looper.getMainLooper()) // 创建第一个Handler val handler2 = Handler() // 创建第二个Handler handler1.sendMessage(Message.obtain(handler2, object : Runnable { override fun run() { // 在第二个Handler线程执行 } })) ``` 在这个例子,我们创建了两个Handler。在第一个Handler的线程,我们使用sendMessage方法将一个Message对象发送到第二个Handler的消息队列,以便在稍后的时间执行。 请注意,这两种方法都可以在Kotlin使用。您可以根据自己的需要选择其一种方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值