99

I've read this post here. But still I cannot run code containing Java 8 Stream API features like the following on minSdkVersion < 24.

List<Car> newCars = cars.stream()
                        .filter(s -> s.getColor().equals("red"))
                        .collect(Collectors.toList());

This doesn't run due to the error message

Call requires API level 24 (current min is 15): java.util.Collection#stream

So does someone know a solution?

Community
  • 1
  • 1
unlimited101
  • 3,653
  • 4
  • 22
  • 41
  • 4
    no you cannot use the java stream below api 24. there are 3rd party libraries that implement the same thing though that work – tyczj Sep 15 '16 at 15:34
  • 1
    I have a small library that does similar things for lower apis: https://github.com/smaspe/FunctionalIterables – njzk2 Sep 15 '16 at 15:36
  • 2
    See http://stackoverflow.com/questions/39265004/is-android-n-stream-api-backported-to-lower-versions and http://stackoverflow.com/questions/39039566/is-it-possible-to-use-java-8-features-optional-and-stream-for-android-14-and-hig for similar questions (and answers). – Sartorius Sep 15 '16 at 15:51
  • 3
    @PEMapModder You **can** use lambda expressions and method references using the new Jack compiler targeting Android as far back as Gingerbread. What you **can't** have below SDK 24 are default / static interface methods and Java 8 specific APIs like the Stream API. – Sartorius Sep 16 '16 at 10:48
  • 1
    You can use StreamSupport class under API 24 – Özer Özcan Apr 24 '20 at 17:25

6 Answers6

85

[original answer]

You can not use Java8 streams on API level < 24.

However, there are some libraries that backport some of the stream functionality

https://github.com/aNNiMON/Lightweight-Stream-API

https://github.com/konmik/solid

https://sourceforge.net/projects/streamsupport/ (mentioned by @sartorius in comment)

[update k3b 2019-05-23]

https://github.com/retrostreams/android-retrostreams is a spinoff from streamsupport which takes advantage of Android Studio 3.x D8 / desugar toolchain's capability to use interface default & static methods across Jar file boundaries. There are also links to other android-retroXXX ie for CompletableFuture.

[update aeracode 2020-07-24]

Good news, now we can use Java 8 Stream API and more without requiring a minimum API level.

æ-ra-code
  • 2,140
  • 30
  • 28
Robert Estivill
  • 12,369
  • 8
  • 43
  • 64
  • 9
    While [solid](https://github.com/konmik/solid) and [Lightweight-Stream-API](https://github.com/aNNiMON/Lightweight-Stream-API) are certainly valuable options for Stream-like programming, IMHO none of them can even remotely be considered a "backport" of the Java 8 Stream API. The only project I know of that can claim to be close in syntax and on par in functionality to Java 8 streams is [streamsupport](https://sourceforge.net/projects/streamsupport/). For example, it also supports parallel streams. Though that might be not so important for Android devs. – Sartorius Sep 15 '16 at 16:19
  • IMO, Lightweight-Stream-API was the nicest on Android because of the Gradle support and added extras like "select", "groupBy", etc. https://github.com/aNNiMON/Lightweight-Stream-API#additional-operators. – LordParsley Feb 28 '17 at 09:09
  • 2
    @LordParsley streamsupport also has Gradle support. As for the extras, yup that's true. As I understand it, extending / diverting from the official Java 8 API has never been a design goal of streamsupport. – Sartorius Mar 26 '17 at 11:59
  • 3
    @Sartorius is right – I recommend checking out the Github mirror for usage details and the Gradle refs https://github.com/streamsupport/streamsupport – LordParsley Mar 30 '17 at 09:54
  • I have read that in general Java Streams are slower than collections. https://stackoverflow.com/questions/22658322/java-8-performance-of-streams-vs-collections What about these backports? – Shikhar Shrivastav Apr 29 '18 at 06:44
16

Yes, it is! Starting from AGP 4.0.0 we can use Stream API and some other Java 8+ APIs (e.g. java.time)

Minimal required changes to your app's build.gradle:

android {
  compileOptions {
    // Flag to enable support for the new language APIs
    coreLibraryDesugaringEnabled true
  }
}

dependencies {
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
}
æ-ra-code
  • 2,140
  • 30
  • 28
15

Since release 8.2 of DexGuard it is possible to use the Java 8 streams API also on Android devices < API level 24. In order to do so, one needs to include the streamsupport library and DexGuard will translate all Java 8 stream API calls to the provided library. No additional handling is needed, and developers can simply code using the provided Java 8 streams API. Dependencies are also translated automatically, so it is possible to use libraries with Java 8 features also for Android development.

This feature will also be included in ProGuard in the near future, stay tuned.

Edit: Proguard 6.1.0 for which there already exists a beta version supports backporting Java 8 stream and time API.

T. Neidhart
  • 6,060
  • 2
  • 15
  • 38
  • it's not official library – user25 Dec 31 '18 at 16:13
  • I have no really idea to the library itself, but I would recommend to check the library's licence before use in a commercial project. _GNU General Public License, version 2, with the Classpath Exception_. Probably a layer has to confirm. – Michal Harakal Feb 08 '19 at 17:36
  • 5
    @MichalHarakal it took me a couple of seconds to realize that *layer* was actually *lawyer* :) – superjos Mar 18 '19 at 10:00
  • Our app crash with desugaring in release version due to proguard, any idea how to fix it? This is the SO question https://stackoverflow.com/q/67656105/12204620 – Bitwise DEVS May 23 '21 at 04:24
6

Might be a little late to the party, but I just saw that If we use java8.util.stream.StreamSupport then we can run the same functional programming approach and use it prior to Api 24. Eg taken from web3j -

StreamSupport.stream(transactionReceipt.getLogs())
            .map(log -> extractEventParameters(event, log))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
Abhriya Roy
  • 1,338
  • 17
  • 23
  • 3
    @AjahnCharles Telling people to just use language B when their question is about language A isn't a good answer. Many projects are still written in Java. – mkuech Mar 24 '20 at 01:50
  • 2
    @AjahnCharles You make a good point about drawing readers' attention to _possibly_ considering changes to the Android ecosystem. That said, the question is about a specific feature of Java which is still an officially supported language for Android development. Switching, even if "only" hybridizing your app, brings a cost (learning curve, maybe their team doesn't support it, etc) and that requires diving into _way_ more than what is being asked in the question. More importantly, the comment doesn't address _how_ Kotlin is a solution, which makes it more out-of-context and less helpful. – mkuech Apr 12 '20 at 21:39
  • 3
    @AjahnCharles when it is clearly mentioned `Java 8 Stream API`, I feel, the answer should also be in JAVA! – Abhriya Roy Apr 14 '20 at 17:15
1

Another solution I like to use is to statically use Kotlin's extensions over Collections from Java code, if the project supports both of course:

List<Car> newCars = CollectionsKt.filter(cars, s -> s.getColor().equals("red"));

Same for .map, .reduce, .first, ...etc

y.allam
  • 1,446
  • 13
  • 24
-6

Create a İnterface.

public interface Pre<T,R> {
            R get(T item);
        }

Create a Filter Method.

public static  <T> List<T> Filter(List<T> list, Pre<T,Boolean> pre) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
        {
            return list.stream().filter(p -> pre.get(p)).collect(Collectors.toList());
        }
        else
        {
            List<T> col = new ArrayList<T>();
            for (int i = 0 ; i < list.size() ; i++)
                if (pre.get(list.get(i)))
                    col.add(list.get(i));
            return col;
        }
    }

Using Code

    public static class model {
            public String Name;
        }
List<model> models = new ArrayList<>();
            ...
            List<model> filtermodels = Filter(models,p-> p.Name.equals("filter"));