Плагин Android Gradle Build 4.0.0 и R8 Desugaring не работает с API 19

Я переключаю приложение Android с использования десугарирования Proguard на новое удаление сахара R8, доступное в Android Gradle Build Plugin 4.0.0.

Я выполнил шаги, подробно описанные в официальной документации, чтобы включить удаление сахара из библиотеки Java 8:

gradle.properties

projectJavaVersion = 1.8
android.useAndroidX=true
android.enableJetifier=true

приложение build.gradle

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}
android {
    buildToolsVersion '29.0.2'
    compileSdkVersion 29
    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility projectJavaVersion
        targetCompatibility projectJavaVersion
    }
    kotlinOptions {
        jvmTarget = projectJavaVersion
    }
    defaultConfig {
        multiDexEnabled true
        minSdkVersion 19
        targetSdkVersion 23
        applicationId = 'com.example.app'
    }
    buildTypes {
        release {
            ...
            minifyEnabled true
        }
        debug {
            debuggable true
            minifyEnabled true
        }
    }
}
dependencies {
    coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.0.10"
    ...
}

Как видите, мы как минимум поддерживаем API 19. Ошибок сборки нет (с использованием Gradle 6.1.1), но есть следующие предупреждения:

Warning in synthesized for lambda desugaring:
  Type `j$.$r8$wrapper$java$util$function$Function$-V-WRP` was not found, it is required for default or static interface methods desugaring of `java.util.Comparator java.time.chrono.-$$Lambda$AbstractChronology$j22w8kHhJoqCd56hhLQK1G0VLFw.thenComparing($-vivified-$.java.util.function.Function)`
Warning in synthesized for lambda desugaring:
  Type `j$.$r8$wrapper$java$util$function$ToLongFunction$-V-WRP` was not found, it is required for default or static interface methods desugaring of `java.util.Comparator java.time.chrono.-$$Lambda$AbstractChronology$j22w8kHhJoqCd56hhLQK1G0VLFw.thenComparingLong($-vivified-$.java.util.function.ToLongFunction)`
Warning in synthesized for lambda desugaring:
  Type `j$.$r8$wrapper$java$util$function$ToDoubleFunction$-V-WRP` was not found, it is required for default or static interface methods desugaring of `java.util.Comparator java.time.chrono.-$$Lambda$AbstractChronology$j22w8kHhJoqCd56hhLQK1G0VLFw.thenComparingDouble($-vivified-$.java.util.function.ToDoubleFunction)`
Warning in synthesized for lambda desugaring:
  Type `j$.$r8$wrapper$java$util$function$ToIntFunction$-V-WRP` was not found, it is required for default or static interface methods desugaring of `java.util.Comparator java.time.chrono.-$$Lambda$AbstractChronology$j22w8kHhJoqCd56hhLQK1G0VLFw.thenComparingInt($-vivified-$.java.util.function.ToIntFunction)`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ConcurrentHashMap$EntryIterator.class:
  Type `j$.$r8$wrapper$java$util$function$Consumer$-V-WRP` was not found, it is required for default or static interface methods desugaring of `void java.util.concurrent.ConcurrentHashMap$EntryIterator.forEachRemaining($-vivified-$.java.util.function.Consumer)`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ConcurrentHashMap$CollectionView.class:
  Type `j$.$r8$wrapper$java$util$stream$Stream$-WRP` was not found, it is required for default or static interface methods desugaring of `$-vivified-$.java.util.stream.Stream java.util.concurrent.ConcurrentHashMap$CollectionView.parallelStream()`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ConcurrentHashMap$CollectionView.class:
  Type `j$.$r8$wrapper$java$util$Spliterator$-WRP` was not found, it is required for default or static interface methods desugaring of `$-vivified-$.java.util.Spliterator java.util.concurrent.ConcurrentHashMap$CollectionView.spliterator()`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ConcurrentHashMap$CollectionView.class:
  Type `j$.$r8$wrapper$java$util$function$Predicate$-V-WRP` was not found, it is required for default or static interface methods desugaring of `boolean java.util.concurrent.ConcurrentHashMap$CollectionView.removeIf($-vivified-$.java.util.function.Predicate)`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/DesugarCollections$SynchronizedMap.class:
  Type `j$.$r8$wrapper$java$util$function$BiFunction$-V-WRP` was not found, it is required for default or static interface methods desugaring of `void java.util.DesugarCollections$SynchronizedMap.replaceAll($-vivified-$.java.util.function.BiFunction)`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/DesugarCollections$SynchronizedMap.class:
  Type `j$.$r8$wrapper$java$util$function$BiConsumer$-V-WRP` was not found, it is required for default or static interface methods desugaring of `void java.util.DesugarCollections$SynchronizedMap.forEach($-vivified-$.java.util.function.BiConsumer)`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ThreadLocalRandom.class:
  Type `j$.$r8$wrapper$java$util$stream$IntStream$-WRP` was not found, it is required for default or static interface methods desugaring of `$-vivified-$.java.util.stream.IntStream java.util.concurrent.ThreadLocalRandom.ints()`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ThreadLocalRandom.class:
  Type `j$.$r8$wrapper$java$util$stream$LongStream$-WRP` was not found, it is required for default or static interface methods desugaring of `$-vivified-$.java.util.stream.LongStream java.util.concurrent.ThreadLocalRandom.longs()`
Warning in /Users/username/.gradle/caches/modules-2/files-2.1/com.android.tools/desugar_jdk_libs/1.0.4/961afbdb3d41eebfb63b8c8ccdc97453b869964e/desugar_jdk_libs-1.0.4.jar:java/util/concurrent/ThreadLocalRandom.class:
  Type `j$.$r8$wrapper$java$util$stream$DoubleStream$-WRP` was not found, it is required for default or static interface methods desugaring of `$-vivified-$.java.util.stream.DoubleStream java.util.concurrent.ThreadLocalRandom.doubles(long)`

Warning: Type `java.util.OptionalConversions` was not found, it is required for default or static interface methods desugaring of `java.util.OptionalLong j$.$r8$wrapper$java$util$stream$LongStream$-WRP.findAny()`
Warning: Type `java.util.LongSummaryStatisticsConversions` was not found, it is required for default or static interface methods desugaring of `java.util.LongSummaryStatistics j$.$r8$wrapper$java$util$stream$LongStream$-WRP.summaryStatistics()`
Warning: Type `java.util.IntSummaryStatisticsConversions` was not found, it is required for default or static interface methods desugaring of `java.util.IntSummaryStatistics j$.$r8$wrapper$java$util$stream$IntStream$-WRP.summaryStatistics()`
Warning: Type `java.util.DoubleSummaryStatisticsConversions` was not found, it is required for default or static interface methods desugaring of `java.util.DoubleSummaryStatistics j$.$r8$wrapper$java$util$stream$DoubleStream$-WRP.summaryStatistics()`

Когда я запускаю приложение, запуская действие со следующим Необязательным в коде, приложение вылетает с

I/ApplicationBase: Testing Java 8
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    Process: com.example.app.DEV, PID: 8265
    java.lang.RuntimeException: An error occured while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:300)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
        at java.util.concurrent.FutureTask.run(FutureTask.java:242)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:841)
     Caused by: java.lang.NoClassDefFoundError: j$.util.Optional
        at com.example.app.ApplicationBase.launch(ApplicationBase.java:251)
        at com.example.app.LaunchTask.doInBackground(LaunchTask.java:19)
        at com.example.app.LaunchTask.doInBackground(LaunchTask.java:6)
        at android.os.AsyncTask$2.call(AsyncTask.java:288)

ApplicationBase.java

import androidx.multidex.MultiDexApplication
import java.util.Optional;

public class ApplicationBase extends MultiDexApplication implements LaunchTask.Launcher {
  @SuppressLint("NewApi")
  @Override
  public void launch() throws IOException {
    Logger.i(TAG, "Testing Java 8");
    Optional.of("xyz").ifPresent(__ -> Logger.i(TAG, "Good"));
    Logger.i(TAG, "Tested Java 8");
    ...
  }
  ...
}

Что мне не хватает?

5 ответов

Я столкнулся с той же проблемой в нашем устаревшем приложении, которое по-прежнему поддерживает Android 4.1. После того как я создал образец проекта и попытался воспроизвести ошибку, я обнаружил проблему (или ошибку?).

К сожалению, похоже, что устройства Android с версией 4.4 или ниже не могут использовать какой-либо класс или интерфейс Java 8, которые будут затронуты с помощью Core Library Desugaring внутри метода onCreate приложения.

Похоже на странную проблему / ошибку с мультидексингом.

Я пробовал следующее:

      class CoreApplication : MultiDexApplication() {
    override fun attachBaseContext(base: Context?) {
        //adding the new GMS causes to many Methods in APK, therefore configure Application as MultiDex
        super.attachBaseContext(base)
        MultiDex.install(this)
    }

    override fun onCreate() {
        super.onCreate()
        Optional.of("TEst")
    }
}

Что приведет к упомянутому сбою:

      03-22 18:36:09.231 657-657/com.plauzeware.CoreLibraryDesugeringCrashOnAndroidKitkat E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.plauzeware.CoreLibraryDesugeringCrashOnAndroidKitkat, PID: 657
    java.lang.NoClassDefFoundError: j$.util.Optional
        at com.plauzeware.corelibrarydesugeringcrashonandroidkitkat.CoreApplication.onCreate(Application.kt:17)
        at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1030)
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4409)
        at android.app.ActivityThread.access$1500(ActivityThread.java:139)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1270)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5086)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
        at dalvik.system.NativeStart.main(Native Method)

Но когда я завершаю вызов Optional внутри вспомогательного класса

      class Loader {
    companion object {
        fun load() {
            Optional.of("TEst")
        }
    }
}

и назовите его внутри моей функции onCreate

      class CoreApplication : MultiDexApplication() {
    override fun attachBaseContext(base: Context?) {
        //adding the new GMS causes to many Methods in APK, therefore configure Application as MultiDex
        super.attachBaseContext(base)
        MultiDex.install(this)
    }

    override fun onCreate() {
        super.onCreate()
        Loader.load()
    }
}

это будет работать. Я думаю, что это ошибка, и я отправлю отчет об ошибке и добавлю в ответ ссылку на нее.

Поскольку проблема мультиплексирования затрагивает также базовые интерфейсы, исправлять эти ошибки очень сложно. Тем более, что мы все еще используем OrmLite, и он будет использовать Iterable все время.

Я добавил свой код для ссылки на свой Github

Я обновил версию своегоcom.android.tools.build:gradleсогласно этой таблице

Я столкнулся с аналогичной проблемой, и в моем случае оказалось, что это ошибка в Android Gradle Plugin. Короче говоря, AGP генерирует несколько одинаковых классов-оболочек в разных DEX-файлах, которые взрывают Dalvik/ART в старых версиях Android. И проблема была только в отладочной сборке, потому что R8/ProGuard дедуплицирует эти классы в сборках выпуска.

И есть обходной путь для этой проблемы:

buildscript {

    repositories {
        maven {
            url "https://storage.googleapis.com/r8-releases/raw/master" // NOTICE 'master' here!
        }
    }

    dependencies {
        classpath 'com.android.tools:r8:f03be11f11b8405b69876d05337e917a5519e52a'  // Must be before the Gradle Plugin for Android.
        classpath 'com.android.tools.build:gradle:X.Y.Z'     // Your current AGP version.
     }
}

Надеюсь, это вам поможет.

Это было решено обновлением до Android Gradle Build Plugin 4.2.0, где R8 правильно десахарирует.

Если сбой наблюдается только на устройствах с уровнем API 19, причина может заключаться в том, что MultiDex.install не вызывался до использования обессахариваемых типов.

Реализация java.util.Optional (который называется j$.util.Optionalв обессахаренной библиотеке) находится в отдельном файле DEX. На устройствах, которые не поддерживают собственный multi dex (который был добавлен на уровне API 21) MultiDex.installдолжен быть вызван перед использованием обессахаренных типов. В MultiDexApplication призыв к MultiDex.install в attachBaseContext. Если launch метод вызывается перед MultiDex.install вы увидите NoClassDefFoundError.

Другие вопросы по тегам