今天在调试一个 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 的道理啊。
在多方百度、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