Совместимость лямбда с Android API 21-24

#android #lambda #java-8

Вопрос:

Я разрабатываю приложение для Android, которое использует очень, очень большую стороннюю библиотеку Java (netcdf-java). Библиотека широко использует функции Java 8, в частности лямбда-выражения.

Приложение отлично работает на Oreo/API 25 , но вылетает и сгорает на API 21-24 (21 был самым старым, который я удосужился протестировать) с ошибкой, которая, по-видимому, является ошибкой NoClassDefFoundError, связанной с лямбдами, после нескольких неудачных загрузок классов, которые я прочитал, в основном означают: «Эта версия Android не может выполнять лямбды».:

 06-14 15:43:05.395 8010-8127/pantherkitty.launchspotter I/art: Rejecting re-init on previously-failed class java.lang.Class<ucar.nc2.-$Lambda$Group$oysUYWnjlRBOBg2TJn5tet0MooM>
(... snip ...)
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │ caught exception while parsing GriddedData for /data/user/0/pantherkitty.launchspotter/cache/abi-l2-acmc-57529695.nc : java.io.IOException: java.lang.NoClassDefFoundError: ucar.nc2.-$Lambda$Group$oysUYWnjlRBOBg2TJn5tet0MooM
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │  at ucar.nc2.NetcdfFile.open(NetcdfFile.java:401)
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │  at ucar.nc2.dataset.NetcdfDataset.openProtocolOrFile(NetcdfDataset.java:831)
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │  at ucar.nc2.dataset.NetcdfDataset.openDataset(NetcdfDataset.java:479)
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │  at ucar.nc2.dataset.NetcdfDataset.acquireDataset(NetcdfDataset.java:634)
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │  at ucar.nc2.dt.grid.GridDataset.open(GridDataset.java:86)
06-14 15:43:05.397 8010-8127/pantherkitty.launchspotter E/PRETTY_LOGGER: │  at ucar.nc2.dt.grid.GridDataset.open(GridDataset.java:72)
 

Я читал, что Google якобы изменил Android Studio/Gradle, чтобы сделать функции Java 8 обратно совместимыми со старыми устройствами, ограниченными более ранними API. Теорию в сторону… Я никогда не видел, чтобы это действительно работало, несмотря на использование последней версии Android Studio, обновление проекта до Gradle 3, таргетинг API=30 (с минимальным API=21) и указание JavaVersion.VERSION_1_8 для совместимости с исходным кодом и целевой совместимостью в build.gradle.

Так… что-то еще мне нужно сделать, чтобы обеспечить беспрепятственный лямбда-загрузка под управлением Android приложений, работающих по АПИ 21-24, или 100% обратная совместимость с лямбда-выражениями и старше Android устройств с API только 21-24 (леденец, зефира и нуга) то, что до сих пор не работает… и, вероятно, никогда не буду работать?


обновление: Я применил предложения Артема Витера.

Я больше не получаю ошибок «отклонение повторного ввода в ранее неудачном классе» или «NoClassDefFound» для классов, которые ранее казались связанными с лямбда, но теперь я получаю некоторые новые ошибки, которые еще более загадочны.

Я не уверен, является ли это просто новым проявлением моей первоначальной проблемы (скажем, если я просто вижу сообщение об ошибке, возникающее в результате удаления кода, которое в конечном итоге сводится к одному и тому же), или это что-то совершенно новое и другое, у которого здесь должен быть другой вопрос.

(примечание: Я тестирую его на планшете Chuwi Hi12 с процессором Intel Cherry Trail SoC и Android 5.1, что может объяснить ошибки, связанные с Intel)

 06-14 18:56:35.783 17016-17677/pantherkitty.launchspotter W/art: Failed to open zip archive '/system/framework/com.intel.config.jar': I/O Error
06-14 18:56:35.783 17016-17677/pantherkitty.launchspotter W/art: Failed to open zip archive '/system/framework/com.intel.aware.awareservice.jar': I/O Error
06-14 18:56:35.900 17016-17677/pantherkitty.launchspotter W/art: Failed to open zip archive '/system/framework/com.intel.config.jar': I/O Error
06-14 18:56:35.900 17016-17677/pantherkitty.launchspotter W/art: Failed to open zip archive '/system/framework/com.intel.aware.awareservice.jar': I/O Error
06-14 18:56:35.970 17016-17677/pantherkitty.launchspotter E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    Process: pantherkitty.launchspotter, PID: 17016
    java.lang.RuntimeException: An error occured while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:304)
        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:818)
     Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/invoke/MethodHandles;
        at ucar.nc2.dataset.EnhanceScaleMissingUnsignedImpl.<clinit>(EnhanceScaleMissingUnsignedImpl.java:30)
        at ucar.nc2.dataset.VariableDS.<init>(VariableDS.java:761)
        at ucar.nc2.dataset.NetcdfDataset.convertVariable(NetcdfDataset.java:1172)
        at ucar.nc2.dataset.NetcdfDataset.convertGroup(NetcdfDataset.java:1156)
        at ucar.nc2.dataset.NetcdfDataset.<init>(NetcdfDataset.java:1139)
        at ucar.nc2.dataset.NetcdfDataset.openDataset(NetcdfDataset.java:485)
        at ucar.nc2.dataset.NetcdfDataset.acquireDataset(NetcdfDataset.java:634)
        at ucar.nc2.dt.grid.GridDataset.open(GridDataset.java:86)
        at ucar.nc2.dt.grid.GridDataset.open(GridDataset.java:72)
        at pantherkitty.launchspotter.GriddedData.update(GriddedData.java:45)
        at pantherkitty.launchspotter.MapsActivity$GriddedDataParserTask.doInBackground(MapsActivity.java:242)
        at pantherkitty.launchspotter.MapsActivity$GriddedDataParserTask.doInBackground(MapsActivity.java:221)
        at android.os.AsyncTask$2.call(AsyncTask.java:292)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        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:818) 
     Caused by: java.lang.ClassNotFoundException: Didn't find class "java.lang.invoke.MethodHandles" on path: DexPathList[[zip file "/data/app/pantherkitty.launchspotter-2/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
        at ucar.nc2.dataset.EnhanceScaleMissingUnsignedImpl.<clinit>(EnhanceScaleMissingUnsignedImpl.java:30) 
        at ucar.nc2.dataset.VariableDS.<init>(VariableDS.java:761) 
        at ucar.nc2.dataset.NetcdfDataset.convertVariable(NetcdfDataset.java:1172) 
        at ucar.nc2.dataset.NetcdfDataset.convertGroup(NetcdfDataset.java:1156) 
        at ucar.nc2.dataset.NetcdfDataset.<init>(NetcdfDataset.java:1139) 
        at ucar.nc2.dataset.NetcdfDataset.openDataset(NetcdfDataset.java:485) 
        at ucar.nc2.dataset.NetcdfDataset.acquireDataset(NetcdfDataset.java:634) 
        at ucar.nc2.dt.grid.GridDataset.open(GridDataset.java:86) 
        at ucar.nc2.dt.grid.GridDataset.open(GridDataset.java:72) 
        at pantherkitty.launchspotter.GriddedData.update(GriddedData.java:45) 
        at pantherkitty.launchspotter.MapsActivity$GriddedDataParserTask.doInBackground(MapsActivity.java:242) 
        at pantherkitty.launchspotter.MapsActivity$GriddedDataParserTask.doInBackground(MapsActivity.java:221) 
        at android.os.AsyncTask$2.call(AsyncTask.java:292) 
        at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
        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:818) 
        Suppressed: java.lang.ClassNotFoundException: java.lang.invoke.MethodHandles
        at java.lang.Class.classForName(Native Method)
        at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
        at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
                ... 19 more
     Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
 

обновление 2:

У меня произошел такой же сбой на моем Galaxy S3 (под управлением AOSP Marshmallow), но без четырех ошибок W/art, связанных с файлами Intel jar, которые я получил на планшете. После этого детали самой катастрофы были практически идентичны.

Комментарии:

1. У вас есть следующие сеттинги multiDexEnabled = true , coreLibraryDesugaringEnabled = true и coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.0.4") ( developer.android.com/studio/releases#4-0-0-desugar )

2. Хммм… это изменение определенно что-то дало. Он все еще рушится, но теперь он рушится немного иначе , чем раньше. Мне придется немного покопаться и попытаться разобраться в том, что изменилось.

3. Похоже, это связано. От developer.android.com/studio/write/java8-support : «В настоящее время Desugar не поддерживает MethodHandle.invoke или MethodHandle.invokeExact». В нем упоминается сокращение кода, если вы на самом деле не используете метод, который может быть применим.

4. Тьфу. Вот чего я боялся. Я почти уверен, что помню, как видел один или оба этих метода, на которые ссылались, когда я просматривал код ~3 часа назад в поисках точки, в которой он разбился. Я предполагаю, что для этого мне придется отказаться от совместимости со старыми устройствами, если только я не хочу погрузиться в многонедельную работу, чтобы попытаться переписать фрагменты самой библиотеки. Oreo — самая старая версия Android, которая поддерживает все, начиная с Java 8 (включая метод handle.invoke() без предупреждений или отказов от ответственности, верно?

5. Да, похоже, что версия API 26 (Oreo) — это то, где все это представлено. Стоит ли проверять более старые версии netcdf-java библиотеки, чтобы узнать, будут ли они работать с версиями до 26?