javaandroidandroid-virtual-devicenosuchmethoderrorandroid-runtime

String#indent() NoSuchMethodError in Android Runtime of the Android Studio's emulator


I need to run a project on an Android Studio emulator, but the emulator's JVM is missing a method.

When a source code that has been successfully tested on physical devices (ranging from API 31 - 33) before, is now tested on the AVD Android Studio emulator (Pixel 4 API 34), an Exception occurs:

FATAL EXCEPTION: main
                            Process: com.org.project, PID: 11295
                            java.lang.NoSuchMethodError: No virtual method indent(I)Ljava/lang/String; in class
                            Ljava/lang/String; or its super classes (declaration of 'java.lang.String' appears in
                            /apex/com.android.art/javalib/core-oj.jar)
                                at

These are the emulator properties:

Properties
avd.ini.displayname              Pixel 5 API 33 2
avd.ini.encoding                 UTF-8
AvdId                            Pixel_5_API_33_2
disk.dataPartition.size          6442450944
fastboot.chosenSnapshotFile
fastboot.forceChosenSnapshotBoot no
fastboot.forceColdBoot           no
fastboot.forceFastBoot           yes
hw.accelerometer                 yes
hw.arc                           false
hw.audioInput                    yes
hw.battery                       yes
hw.camera.back                   virtualscene
hw.camera.front                  emulated
hw.cpu.ncore                     4
hw.device.hash2                  MD5:3274126e0242a0d86339850416b0ce34
hw.device.manufacturer           Google
hw.device.name                   pixel_5
hw.dPad                          no
hw.gps                           yes
hw.gpu.enabled                   yes
hw.gpu.mode                      auto
hw.initialOrientation            Portrait
hw.keyboard                      yes
hw.lcd.density                   440
hw.lcd.height                    2340
hw.lcd.width                     1080
hw.mainKeys                      no
hw.ramSize                       1536
hw.sdCard                        yes
hw.sensors.orientation           yes
hw.sensors.proximity             yes
hw.trackBall                     no
image.androidVersion.api         33
image.sysdir.1                   system-images\android-33\google_apis\x86_64\
PlayStore.enabled                false
runtime.network.latency          none
runtime.network.speed            full
showDeviceFrame                  yes
skin.dynamic                     yes
tag.display                      Google APIs
tag.id                           google_apis
vm.heapSize                      228

When checking the Java source code from within the project, the method is there:

    /**
     * a comment explaining the method...
     * @since 12
     */
    public String indent(int n) {
        if (isEmpty()) {
            return "";
        }
        Stream<String> stream = lines();
        if (n > 0) {
            final String spaces = " ".repeat(n);
            stream = stream.map(s -> spaces + s);
        } else if (n == Integer.MIN_VALUE) {
            stream = stream.map(s -> s.stripLeading());
        } else if (n < 0) {
            stream = stream.map(s -> s.substring(Math.min(-n, s.indexOfNonWhitespace())));
        }
        return stream.collect(Collectors.joining("\n", "", "\n"));
    }

I understand that Android's proprietary usage of the standard library stopped at Java 11. Since then, additional Gradle/compiling/plugin options have been added to make use of Java tools that belong to versions greater than 11.

These are the configurations being used in this project:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'com.google.gms.google-services'
}

android {
    namespace 'com.me.myproject'

    compileSdk 34

    defaultConfig {
        applicationId "com.me.myproject"

        minSdkVersion 26

        targetSdkVersion 33

        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '17'
    }
}

dependencies {

    testImplementation 'junit:junit:4.13.2'

    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3' // << This is the main plugin in charge of compatibility.

}

apply plugin: 'kotlin-android'

This specific emulator is working on a machine that's using an Android Studio that manages a Java library that comes bundled with the IDE.

It seems to me Android Studio (and other IntelliJ products) assigning this JDK to every project that's opened in this IDE.

This means that both Gradle, the compiler and even the Java Runtime that's used on the IDE use a "synthetic" JAVA_HOME, but also a synthetic Gradle, that's assigned by the IDE configuration. So that any action that the IDE performs works on these autogenerated global variables, a variable that's assigned one for each project.

This makes some tasks like checking for Java version or running Gradle commands very difficult, as one cannot use the CLI of either Windows, or even the one provided by Android Studio itself under the "Terminal" Tab... as it uses the default Windows PowerShell.

This is why I find that the Gradle documentation is pretty much pointless since the documentation is written with the assumption that the full Gradle program is installed in the machine.

Android Studio has a Gradle plugin, which has a CLI, this CLI (which is rather somewhat hidden) is the only one that works with these autogenerated variables, so, any task that needs to be run on Gradle needs to be done through this CLI.

As for any interaction that's needed with the JVM, the only way I've found to effectively interact with it, is via some UI panels.

One of them in the "project structure", the other on the IDE "Settings", the other by right-clicking on test classes which allows you to define "VM options".

But this issue is not relevant to compile phases, not even to the code itself, it seems to be exclusive to the AVD.

None of these options that let you pick specific JDK's are given for the JVM that runs in the ART (Android Runtime) of the emulator.

I think the answer may be in some of these configurations, maybe the answer is on some build.gradle configuration.


Solution

  • is now tested on the AVD Android Studio emulator (Pixel 4 API 34)

    That does not line up with what you have shown for emulator properties. In particular, those properties suggest that the API level is 33, not 34:

    avd.ini.displayname              Pixel 5 API 33 2
    AvdId                            Pixel_5_API_33_2
    image.androidVersion.api         33
    image.sysdir.1                   system-images\android-33\google_apis\x86_64\
    

    indent() was added to the Android SDK in API Level 34. I would not expect indent() to be found on your emulator image.