在Android API 24以下的版本中,是否有一种使用Java 8函数式接口的方法?

32

我可以使用retrolambda来启用在Android API level <24下使用Lambda表达式。所以这是可行的。

myButton.setOnClickListener(view -> Timber.d("Lambdas work!"));

这也可以工作

Runnable runLater = () -> Timber.d("Lambdas work!");
runLater.run();

但这个不会

Consumer<Integer> runLaterWithInt = (Integer i) -> Timber.d("i = " + i);
runLaterWithInt.accept(3);

最后一个在Android API Level 24上工作正常,但在其他设备上,这段代码会导致崩溃。

java.lang.NoClassDefFoundError: com.retrolambdatry.MainActivity$$Lambda$1

我尝试启用Java 8而不是使用Retrolambda。前两个代码示例仍然有效,但ButterKnife停止工作了。https://developer.android.com/preview/j8-jack.html#configuration这里说支持java.util.function,但是当运行第三个代码时,我仍然遇到了崩溃,这次有点不同。

java.lang.NoClassDefFoundError: com.retrolambdatry.MainActivity$-void_onCreate_android_os_Bundle_savedInstanceState_LambdaImpl1

3
这个问题的标题不太准确;实际上,Lambda表达式已经被向旧版Android版本进行回溯移植了,因此您不需要使用Retrolambda。只需安装最新的Android Studio和构建工具即可。 - Andrew Sun
1
也许标题不太准确。据我所知,如果我有Android N SDK并启用Jack编译选项,则不需要retrolambda。Lambda可以两种方式工作。但是来自“java.util.function”的函数接口仍然无法使用。 - sasha199568
问题在于Jack编译器阻止使用数据绑定库(参见 这里)。这就是为什么目前许多人可能不愿意使用Jack的原因,但Retrolambda作为替代方案可以获取lambda表达式(直到在未来的Android Studio版本中解决这些问题之前)。 - weibeld
6个回答

27

不确定您是否仍需要对此问题的答案,但其他人(例如我自己)可能需要。

从3.0版本开始,Android Studio 本地支持lambda函数和所有API级别上的许多其他Java 8函数,但有些功能(例如函数式接口和java.util.function)仍限制在API 24+上。

在扩展支持之前,android-retrostreams为大多数支持提供了回溯支持。该项目是streamsupport库的“升级版”,您也可以使用该库,并且具有android-retrostreams中的许多功能。streamsupport库支持降至Java 6/7,因此即使您没有AS 3.0+或不针对Java 8,您也可以使用它,但在大多数情况下最好使用android-retrostreams。您可以查看该项目的Javadoc以了解提供的功能,但我使用的亮点是java.util.functionjava.util.Comparator

请注意,包名称中的java被替换为java9,并且某些类和/或方法名称可能略有更改。例如:

java.util.function变为java9.util.function

java.util.Comparator变为java9.util.Comparators(方法名称和调用模式稍有不同,但具有相同的功能)。


5
更正:如果你考虑使用Java 7 Oracle / OpenJDK VM,那么android-retrostreams 无法 在Java 7上运行。但是,只要你使用Android Studio 3.x及其Java 8功能为这些设备开发应用程序(使用的Java版本与当时流行的版本无关),它就可以在API级别15或14的Android设备上工作,前提是你必须使用Android Studio 3.x版本,而不是2.3.x版本。如果你需要一个确实可以在真正的Java 6 / 7虚拟机(如Sun等)上工作的Stream API后移版本,你将需要使用streamsupport - Sartorius
啊,我的错。 android-retrostreams 的 readme 让我觉得它们只是停止支持 Java 6,并且即使您没有明确地针对 Java 8,它也可以用于 Android Studio。不过我从来没有真正尝试过。以防万一,我会进行编辑以澄清这一点。 - VerumCH
在Android Studio 3.x中,针对哪个Java版本并不重要,最终决定你的应用程序是否在Marshmallow等设备上运行的是minSDKVersion。因此,为避免可能的混淆,请将minSDKVersion = 23(Marshmallow)设置为目标,并在Android Studio 3.x中使用android-retrostreams,您就可以放心了。我的意思是,android-retrostreams jar文件无法在Java 7标准版VM(来自Oracle、IBM等的JRE)上运行。 - Sartorius
1
我想说的是,如果您想要使用Java 8语言特性,如Lambda、方法引用或接口默认方法,那么您必须针对Java 8。这完全与minSDKVersion没有关系,只要您不使用特定于Java 8的API调用(比如来自java.util.function的内容)。android-retrostreams为这些Java 8 API带来了替代方案,甚至可以在冰淇淋三明治上使用(它肯定是Java 6)。 - Sartorius
与“retro-lambda”有何不同?@VerumCH编辑:仔细阅读后,发现retrolambda缺乏对第三方库的支持(https://developer.android.com/studio/write/java8-support.html#migrate_from_retrolambda)。我们应该真正使用“android-retrostreams”。 - mochadwi

12

Android支持库(AndroidX)现在具有ConsumerSupplier

遗憾的是,截至撰写本文时,只添加了这两个接口

现在我们有了Kotlin,它不需要您显式指定功能接口:

    fun test() {
        val text = withPrintStream {
            it.println("Header bla bla ")
            it.printf("%s, %s, %s,", "a", "b", "c").println()
        }
    }

    // Equivalent to the following code in Java:
    //     Consumer<PrintStream> action;
    //     action.accept(ps);
    fun withPrintStream(action: (PrintStream) -> Unit): String {
        val byteArrayOutputStream = ByteArrayOutputStream()
        val ps = PrintStream(byteArrayOutputStream)
        action(ps)
        ps.flush()
        return byteArrayOutputStream.toString()
    }

这是一个老问题。Kotlin解决了这个问题,但在提问时Kotlin并不是一个真正的选择。 - sasha199568
2
这个回答对我很有帮助,因为我现在被卡在Java上了。干杯! - lucas.mdo
这仍然很有用,因为在使用Java代码时,它也适用于API级别<24。 - Tjaart
非常有帮助!!! 还需要添加这个库解糖:https://developer.android.com/studio/write/java8-support#library-desugaring - Mr.G

3

Android Gradle插件(AGP)4.0+为低API版本添加了向后兼容的Java8包支持(例如java.util.function,它在Android API 24中被添加):

请参见https://developer.android.com/studio/write/java8-support#library-desugaring

在APP模块中添加以下代码行使我能够使用openAPI-generator 5.1.0生成的java jersey API-SDK模块,并支持Android API < 24(我使用jersey以获得更好的多态性支持)。

android {
  defaultConfig {
    // Required when setting minSdkVersion to 20 or lower
    multiDexEnabled true
  }

  compileOptions {
    // Flag to enable support for the new language APIs
    coreLibraryDesugaringEnabled true
    // Sets Java compatibility to Java 8
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}

2

作为替代方案,Lightweight-Stream-API 还提供了回溯支持。就像上面提到的 android-retrostreams 一样,您需要使用以下方式替换一些包名:

com.annimon.stream.function 替代 java.util.function

com.annimon.stream.ComparatorCompat 替代 java.util.Comparator


1

使用

import androidx.core.util.Consumer;

替代

import java.util.function.Consumer; (requires min API 24)

示例实现

public static void sampleMethod(Consumer<Boolean> isClicked) {
    isClicked.accept(true);
}

示例实现的用法

Utils.sampleMethod(new Consumer<Boolean>() {
        @Override
        public void accept(Boolean isClicked) {

        }
    });

-1
allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}
dependencies {
        compile 'com.github.ipcjs:java-support:0.0.3'
}

代码:ipcjs/java-support


4
这是什么,为什么它有用? - lucidbrot
3
@jpcjs,是的,我已经看过了,但那并不是很好的文档。我的问题仍然存在^^这是什么?只是安卓的完整副本吗?为什么它能工作? - lucidbrot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接