Kotlin 暗坑之 Runnable 和 lambda 表达式的坑

  • Kotlin,Runnable,lambda,
  • 2020.09.15

今天在调试一个 kotlin 代码,困扰了我半天的时间。简单记录一下。

首先简单介绍一下我的代码,一个 Test 类,对外提供 test() 方法,test() 方法会马上往 handler 里 post 一个 runnable ,runnable 会执行某些操作,并且发了一个 postDelayed ,在 1000 毫秒后,会再次执行 runnable。也就是说,这个 Test 类会每隔 1000 毫秒执行一次 done() 里的业务代码。

class Test {
    private val handler = Handler(Looper.myLooper())
    private val runnable = {
        done()
    }
    fun test() {
        handler.removeCallbacks(runnable)
        handler.post(runnable)
    }
    private fun done(){
        // 被省略的业务代码
        handler.removeCallbacks(runnable)
        handler.postDelayed(runnable,1000)
    }
}

在实际项目上运行的时候发现 test() 方法被业务方调用了两次,不过这种操作是被允许的,而意外的情况是被调用两次后,接下来每 1000 毫秒,done() 方法会执行两次,和预期并不相符。从代码上看,test() 方法被连续调用两次,会执行两次 runnable ,但是后续在 postDelayed() 之前会 remove 掉 runnable,没有执行两次 runnable 的道理啊。

反编译 kotlin 字节码

在多方百度、google、stackoverflow 没有效果后,我想到了看看编译后的 kotlin 代码,大致如下:

final class Test$sam$java_lang_Runnable$0 implements Runnable {
   // $FF: synthetic field
   private final Function0 function;
   Test$sam$java_lang_Runnable$0(Function0 var1) {
      this.function = var1;
   }
   // $FF: synthetic method
   public final void run() {
      Intrinsics.checkExpressionValueIsNotNull(this.function.invoke(), "invoke(...)");
   }
}
public final class Test {
   @NotNull
   private final Handler handler = new Handler(Looper.myLooper());
   @NotNull
   private final Function0 runnable = (Function0)(new Function0() {
      // $FF: synthetic method
      // $FF: bridge method
      public Object invoke() {
         this.invoke();
         return Unit.INSTANCE;
      }
      public final void invoke() {
         Test.this.done();
      }
   });
   @NotNull
   public final Handler getHandler() {
      return this.handler;
   }
   @NotNull
   public final Function0 getRunnable() {
      return this.runnable;
   }
   public final void test() {
      Handler var10000 = this.handler;
      Object var10001 = this.runnable;
      Object var1;
      if (var10001 != null) {
         var1 = var10001;
         var10001 = new Test$sam$java_lang_Runnable$0((Function0)var1);
      }
      var10000.removeCallbacks((Runnable)var10001);
      var10000 = this.handler;
      var10001 = this.runnable;
      if (var10001 != null) {
         var1 = var10001;
         var10001 = new Test$sam$java_lang_Runnable$0((Function0)var1);
      }
      var10000.post((Runnable)var10001);
   }
   public final void done() {
      Handler var10000 = this.handler;
      Object var10001 = this.runnable;
      Object var1;
      if (var10001 != null) {
         var1 = var10001;
         var10001 = new Test$sam$java_lang_Runnable$0((Function0)var1);
      }
      var10000.removeCallbacks((Runnable)var10001);
      var10000 = this.handler;
      var10001 = this.runnable;
      if (var10001 != null) {
         var1 = var10001;
         var10001 = new Test$sam$java_lang_Runnable$0((Function0)var1);
      }
      var10000.postDelayed((Runnable)var10001, 1000L);
   }
}

可以发现,它先帮我生成了一个 Test$sam$java_lang_Runnable$0 类,实现了 Runnable ,而我写的 runnable 其实是 Function0 类。然后细看一下 done() 方法,会发现被 remove 掉的是一个被 new 出来的 Test$sam$java_lang_Runnable$0 Runnable。看到这里,答案基本可以揭晓了。

原来 lambda 表达式的 runnable 并不是 Runnable ,而是 Function0 。

fun done(){
    handler.removeCallbacks(runnable)
    handler.postDelayed(runnable,1000)
}
public final void done() {
      Handler var10000 = this.handler;
      Object var10001 = this.runnable;
      Object var1;
      if (var10001 != null) {
         var1 = var10001;
         var10001 = new Test$sam$java_lang_Runnable$0((Function0)var1);
      }
      var10000.removeCallbacks((Runnable)var10001);
      var10000 = this.handler;
      var10001 = this.runnable;
      if (var10001 != null) {
         var1 = var10001;
         var10001 = new Test$sam$java_lang_Runnable$0((Function0)var1);
      }
      var10000.postDelayed((Runnable)var10001, 1000L);
   }

修改

修改 runnable 的写法,把一个 lambda 表达式修改为一个 Runnable 实现。

class Test {
    val handler = Handler(Looper.myLooper())
    val runnable = Runnable{
        done()
    }
    fun test() {
        handler.removeCallbacks(runnable)
        handler.post(runnable)
    }
    fun done(){
        handler.removeCallbacks(runnable)
        handler.postDelayed(runnable,1000)
    }
}

反编译后的代码:

public final class Test {
   private final Handler handler = new Handler(Looper.myLooper());
   private final Runnable runnable = (Runnable)(new Runnable() {
      public final void run() {
         Test.this.done();
      }
   });
   public final void test() {
      this.handler.removeCallbacks(this.runnable);
      this.handler.post(this.runnable);
   }
   private final void done() {
      this.handler.removeCallbacks(this.runnable);
      this.handler.postDelayed(this.runnable, 1000L);
   }
}

修改后的代码就符合预期了。

每个知识点就像一个黑盒,没有打开它的时候,你对它充满了恐惧,打开后它就变成了白盒,顿时豁然开朗。

相关文章

- EOF -

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处,尊重他人劳动。
转载请注明:文章转载自 Binkery 技术博客 [https://binkery.com]
本文标题: Kotlin 暗坑之 Runnable 和 lambda 表达式的坑
本文地址: https://binkery.com/archives/2020.09.15-一个runnable和lambda表达式的坑.html

赞助商
网站统计
  • 网站运行:3318 天
  • 文章总数:738 篇
  • 网站统计