Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android app building with the wrong JDK(?) somehow

I've recently updated my Android project for Android Studio 3. I wanted to support Java 8 language features, so added the following to build.gradle:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

I then run my app on an Android 8.0.0 device. At runtime I see

java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentHashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in /system/framework/core-oj.jar)

I gather this is to do with the fact that the signature of keySet() was changed in Java 8 from returning Set<K> to returning KeySetView<K,V>.

The line that has caused the exception looks like this:

for (Long id : mSomeMap.keySet())

KeySetView implements Set, it's certainly Iterable, so whether this line is interpreted as Java 7 or Java 8 I'd have thought it would work either way. My understanding of Java fundamentals is sketchy - what's going on here?

Update

My flaky understanding so far is this:

While Android now supports some Java 8 language features, its API is not identical to Java 8. In particular, Android's implementation of ConcurrentHashMap.keySet() returns a Set, while the Java 8 implementation of ConcurrentHashMap.keySet() returns a KeySetView.

Somehow, Android Studio has managed to compile my app with the standard Java 8 JDK and therefore at runtime expects to find a method with the signature KeySetView<K,V> keySet(). However, Android's ConcurrentHashMap does not have a method with this signature, so I get a NoSuchMethodError.

I'm no closer to working out how or why Android Studio is building with an incompatible JDK. In Project Structure, 'Use embedded JDK (recommended)' is checked, so I assume Android Studio is building with the JDK that came bundled with it.

Not a solution

Most comments/answers so far have pointed out that I can just declare the ConcurrentHashMap as a Map to get around this. That is a workaround, and not a solution. If the underlying problem is that my app is being built with the wrong JDK, then there may be other instances where method signatures diverge, and since I can't live test 100% of code paths in what is a large project, I can't guarantee that more NoSuchMethodErrors won't be thrown at run time.

like image 689
UtterlyConfused Avatar asked Aug 23 '17 16:08

UtterlyConfused


People also ask

How to change the java version in Android Studio?

Set the JDK version Open your project in Android Studio and select File > Settings... > Build, Execution, Deployment > Build Tools > Gradle (Android Studio > Preferences... > Build, Execution, Deployment > Build Tools > Gradle on a Mac). Under Gradle JDK, choose the Embedded JDK option. Click OK.

Is JDK included in Android Studio?

A copy of the latest OpenJDK comes bundled with Android Studio 2.2 and higher, and this is the JDK version we recommend you use for your Android projects.

What is config or installation folder in Android Studio?

Contrary to JetBrains' documentation, but according to Configure Android Studio, the configuration is currently under a "Google" folder: Windows: %APPDATA%\Google\<product><version> Mac: ~/Library/Application Support/Google/<product><version> Linux: ~/.

What Java does Android use?

Java 8 source code that works in latest version of Android, can be made to work in older versions of Android.


1 Answers

I'm using this workaround and seems is working.

  private final Map<ImageView, Request> mPendingRequests =
            new ConcurrentHashMap<ImageView, Request>();
like image 109
Javier Avatar answered Oct 13 '22 00:10

Javier