Android中注意使用Kotlin forEach

本文探讨了在Android低版本设备上使用Kotlin的forEach方法时遇到的崩溃问题,并提供了解决方案。原因是由于API24之前的版本不支持Java 8的forEach特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值