Android中注意使用Kotlin forEach

Android中注意使用Kotlin forEach

最近开始使用kotlin,因为一直使用的是Android 7.0测试手机,所有没有发现问题。
然后今天准备在低版本测试一下后发现打开某个页面崩溃了,

java.lang.NoClassDefFoundError: xxx.xxx$lambda$1

很奇怪,开始以为的MultiDex 导致的,但是这个崩溃并不是在启动就闪退,而是过了几个页面才出现的。
根据代码行数发现是在一个map.forEach处,

代码如下,很简单的循环

data.forEach { k,v ->
 //
}

先说解决方案吧
将代码改为如下这样

data.forEach { (k, v) ->
 //
}

原因:
我们先复现场景

fun test1(){
        hashMapOf("a" to "b").forEach { t, u ->

        }
    }


    fun test2(){
        hashMapOf("a" to "b").forEach { (t, u) ->

        }
    }

根据上面的情况,test1()方法在API24之前会崩溃,而test2()正常,我们看下编译之后的smali代码.

先看test1()的

.method public final test1()V
new-array v0, v0, [Lkotlin/Pair;


    invoke-static {v0}, Lkotlin/collections/MapsKt;->hashMapOf([Lkotlin/Pair;)Ljava/util/HashMap;

    move-result-object v1

    check-cast v0, Ljava/util/function/BiConsumer;

    invoke-virtual {v1, v0}, Ljava/util/HashMap;->forEach(Ljava/util/function/BiConsumer;)V

    return-void
.end method

很容易发现kotlin使用了java8的forEach,java8的forEach是通过java.util.function.BiConsumer 实现的,这个类在API24之前没有,所以会崩溃。

我们再看test2()方法生成的smail代码

.method public final test2()V

    invoke-static {v4}, Lkotlin/collections/MapsKt;->hashMapOf([Lkotlin/Pair;)Ljava/util/HashMap;

    move-result-object v0

    check-cast v0, Ljava/util/Map;

    .line 19
    .local v0, "$receiver$iv":Ljava/util/Map;
    invoke-interface {v0}, Ljava/util/Map;->entrySet()Ljava/util/Set;

    move-result-object v4

    invoke-interface {v4}, Ljava/util/Set;->iterator()Ljava/util/Iterator;

    move-result-object v5

    :goto_1c
    invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z

    move-result v4

    if-eqz v4, :cond_37

    invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;

    move-result-object v2

    check-cast v2, Ljava/util/Map$Entry;

    .local v2, "element$iv":Ljava/util/Map$Entry;
    move-object v1, v2

    .local v1, "$t_u":Ljava/util/Map$Entry;
    invoke-interface {v1}, Ljava/util/Map$Entry;->getKey()Ljava/lang/Object;

    move-result-object v3

    check-cast v3, Ljava/lang/String;

    .local v3, "t":Ljava/lang/String;
    invoke-interface {v1}, Ljava/util/Map$Entry;->getValue()Ljava/lang/Object;

    move-result-object v4

    check-cast v4, Ljava/lang/String;

    .line 16
    nop

    goto :goto_1c

    .line 20
    .end local v1    # "$t_u":Ljava/util/Map$Entry;
    .end local v2    # "element$iv":Ljava/util/Map$Entry;
    .end local v3    # "t":Ljava/lang/String;
    :cond_37
    nop

    .line 17
    return-void
.end method

不难看出,这种情况下使用的是Iterator 循环,所以可以正常运行。

参考:
https://developer.android.com/studio/write/java8-support.html
https://stackoverflow.com/questions/42869086/java-lang-noclassdeffounderror-inlinedforeachlambda1-in-kotlin
http://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/

你可能感兴趣的:(kotlin)