Android build error java version

Как Java 8 поддерживается в Android

Привет, Хабр! Предлагаю вашему вниманию перевод замечательной статьи из цикла статей небезызвестного Джейка Вортона о том, как происходит поддержка Андроидом Java 8.

Оригинал статьи лежит тут

Несколько лет я работал из дома, и мне часто приходилось слышать, как мои коллеги жалуются на поддержку Андроидом разных версий Java.

Это довольно сложная тема. Для начала нужно определиться, что мы вообще подразумеваем под «поддержкой Java в Android», ведь в одной версии языка может быть много всего: фичи (лямбды, например), байткод, тулзы, APIs, JVM и так далее.

Когда говорят о поддержке Java 8 в Android, обычно подразумевают поддержку фичей языка. Итак, начнем с них.

Лямбды

Одним из главных нововведений Java 8 были лямбды.
Код стал более лаконичным и простым, лямбды избавили нас от необходимости писать громоздкие анонимные классы, используя интерфейс с единственным методом внутри.

После компиляции этого, используя javac и легаси dx tool , мы получим следующую ошибку:

Эта ошибка происходит из-за того, что лямбды используют новую инструкцию в байткоде — invokedynamic , которая была добавлена в Java 7. Из текста ошибки можно увидеть, что Android поддерживает ее только начиная с 26 API (Android 8).

Звучит не очень, ведь вряд ли кто-то будет выпускать приложение с 26 minApi. Чтобы это обойти, используется так называемый процесс десахаризации (desugaring), который делает возможным поддержку лямбд на всех версиях API.

История десахаризации

Она довольно красочна в мире Android. Цель десахаризации всегда одна и та же — позволить новым языковым фичам работать на всех устройствах.

Изначально, например, для поддержки лямбд в Android разработчики подключали плагин Retrolambda. Он использовал тот же встроенный механизм, что и JVM, конвертируя лямбды в классы, но делал это в рантайме, а не во время компиляции. Сгенерированные классы были очень дорогими с точки зрения количества методов, но со временем, после доработок и улучшений, этот показатель снизился до чего-то более-менее разумного.

Затем команда Android анонсировала новый компилятор, который поддерживал все фичи Java 8 и был более производительным. Он был построен поверх Eclipse Java компилятора, но вместо генерации Java-байткода генерировал Dalvik-байткод. Однако его производительность все равно оставляла желать лучшего.

Когда новый компилятор (к счастью) забросили, трансформатор Java байткода в Java байткод, который и выполнял дешугаринг, был интегрирован в Android Gradle Plugin из Bazel — системы сборки Google. И его производительность все равно была невелика, поэтому параллельно продолжался поиск более хорошего решения.

И вот нам представили новый dexer — D8, который должен был заменить dx tool . Десахаризация теперь выполнялась во время конвертации скомпилированных JAR-файлов в .dex (dexing). D8 сильно выигрывает в производительности по сравнению с dx , и, начиная с Android Gradle Plugin 3.1 он стал dexer’ом по умолчанию.

Теперь, используя D8, у нас получится скомпилировать приведенный выше код.

Чтобы посмотреть, как D8 преобразовал лямбду, можно использовать dexdump tool , который входит в Android SDK. Она выведет довольно много всего, но мы заострим внимание только на этом:

Если вы до этого еще не читали байткод, не волнуйтесь: многое из того, что здесь написано, можно понять интуитивно.

В первом блоке наш main метод с индексом 0000 получает ссылку от поля INSTANCE на класс Java8$1 . Этот класс был сгенерирован во время десахаризации . Байткод метода main тоже нигде не содержит упоминаний о теле нашей лямбды, поэтому, скорее всего, она связана с классом Java8$1 . Индекс 0002 затем вызывает static-метод sayHi , используя ссылку на INSTANCE . Методу sayHi требуется Java8$Logger , поэтому, похоже, Java8$1 имплементирует этот интерфейс. Мы можем убедиться в этом тут:

Читайте также:  Как узнать чип андроид

Флаг SYNTHETIC означает, что класс Java8$1 был сгенерирован и список интерфейсов, которые он включает, содержит Java8$Logger .
Этот класс и представляет собой нашу лямбду. Если вы посмотрите на реализацию метода log , то не увидите тело лямбды.

Вместо этого внутри вызывается static метод класса Java8 — lambda$main$0 . Повторюсь, этот метод представлен только в байткоде.

Флаг SYNTHETIC снова говорит нам, что этот метод был сгенерирован, и его байткод как раз содержит тело лямбды: вызов System.out.println . Причина, по которой тело лямбды находится внутри Java8.class, простая — ей может понадобиться доступ к private членам класса, к которым сгенерированный класс иметь доступа не будет.

Все, что нужно для понимания того, как работает десахаризация, описано выше. Однако, взглянув на это в байткоде Dalvik, можно увидеть, что там все намного более сложно и пугающе.

Преобразование исходников — Source Transformation

Чтобы лучше понимать, как происходит десахаризация, давайте попробуем шаг за шагом преобразовывать наш класс в то, что будет работать на всех версиях API.

Возьмем за основу тот же класс с лямбдой:

Сначала тело лямбды перемещается в package private метод.

Затем генерируется класс, имплементирующий интерфейс Logger , внутри которого выполняется блок кода из тела лямбды.

Далее создается синглтон инстанс Java8$1 , который хранится в static переменной INSTANCE .

Вот итоговый задешугаренный класс, который может использоваться на всех версиях API:

Если вы посмотрите на сгенерированный класс в байткоде Dalvik, то не найдете имен по типу Java8$1 — там будет что-то вроде -$$Lambda$Java8$QkyWJ8jlAksLjYziID4cZLvHwoY . Причина, по которой для класса генерируется такой нейминг, и в чем его плюсы, тянет на отдельную статью.

Нативная поддержка лямбд

Когда мы использовали dx tool , чтобы скомпилировать класс, содержащий лямбды, сообщение об ошибке говорило, что это будет работать только с 26 API.

Поэтому кажется логичным, что если мы попробуем скомпилировать это с флагом —min-api 26 , то десахаризации происходить не будет.

Однако если мы сдампим .dex файл, то в нем все равно можно будет обнаружить -$$Lambda$Java8$QkyWJ8jlAksLjYziID4cZLvHwoY . Почему так? Это баг D8?

Чтобы ответить на этот вопрос, а также почему десахаризация происходит всегда, нам нужно заглянуть внутрь Java-байткода класса Java8 .

Внутри метода main мы снова видим invokedynamic по индексу 0 . Второй аргумент в вызове — 0 — индекс ассоциируемого с ним bootstrap метода.

Вот список bootstrap методов:

Здесь bootstrap метод назван metafactory в классе java.lang.invoke.LambdaMetafactory . Он живет в JDK и занимается созданием анонимных классов налету (on-the-fly) в рантайме для лямбд так же, как и D8 генерит их в компайлтайме.

Если взглянуть на документацию Android к java.lang.invoke
или на исходники AOSP к java.lang.invoke , увидим, что в рантайме этого класса нет. Вот поэтому дешугаринг всегда происходит во время компиляции, независимо от того, какой у вас minApi. VM поддерживает байткод инструкцию, похожую на invokedynamic , но встроенный в JDK LambdaMetafactory недоступен для использования.

Method References

Вместе с лямбдами в Java 8 добавили ссылки на методы — это эффективный способ создать лямбду, тело которой ссылается на уже существующий метод.

Наш интерфейс Logger как раз является таким примером. Тело лямбды ссылалось на System.out.println . Давайте превратим лямбду в метод референc:

Когда мы это скомпилируем и взглянем на байткод, то увидим одно различие с предыдущей версией:

Вместо вызова сгенерированного Java8.lambda$main$0 , который содержит вызов System.out.println , теперь System.out.println вызывается напрямую.

Класс с лямбдой больше не static синглтон, а по индексу 0000 в байткоде видно, что мы получаем ссылку на PrintStream — System.out , который затем используется для того, чтобы вызвать на нем println .

Читайте также:  Android box golf 7

В итоге наш класс превратился в это:

Default и static методы в интерфейсах

Еще одним важным и серьезным изменением, которое принесла Java 8, стала возможность объявлять default и static методы в интерфейсах.

Все это тоже поддерживается D8. Используя те же инструменты, что и ранее, несложно увидеть задешугаренную версию Logger’a с default и static методами. Одно из различий с лямбдами и method references в том, что дефолтные и статик методы реализованы в Android VM и, начиная с 24 API, D8 не будет дешугарить их.

Может, просто использовать Kotlin?

Читая статью, большинство из вас, наверное, подумали о Kotlin. Да, он поддерживает все фичи Java 8, но реализованы они kotlinc точно так же, как и D8, за исключением некоторых деталей.

Поэтому поддержка Андроидом новых версий Java до сих пор очень важна, даже если ваш проект на 100% написан на Kotlin.

Не исключено, что в будущем Kotlin перестанет поддерживать байткод Java 6 и Java 7. IntelliJ IDEA, Gradle 5.0 перешли на Java 8. Количество платформ, работающих на более старых JVM, сокращается.

Desugaring APIs

Все это время я рассказывал про фичи Java 8, но ничего не говорил о новых API — стримы, CompletableFuture , date/time и так далее.

Возвращаясь к примеру с Logger’ом, мы можем использовать новый API даты/времени, чтобы узнать, когда сообщения были отправлены.

Снова компилируем это с помощью javac и преобразуем его в байткод Dalvik с D8, который дешугарит его для поддержки на всех версиях API.

Можете даже запушить это на свой девайс, чтобы убедиться, что оно работает.

Если на этом устройстве API 26 и выше, появится месседж Hello. Если нет — увидим следующее:

D8 справился с лямбдами, метод референсами, но не сделал ничего для работы с LocalDateTime , и это очень печально.

Разработчикам приходится использовать свои собственные реализации или обертки над date/time api, либо использовать библиотеки по типу ThreeTenBP для работы со временем, но почему то, что ты можешь написать руками, не может сделать D8?

Эпилог

Отсутствие поддержки всех новых API Java 8 остается большой проблемой в экосистеме Android. Ведь вряд ли каждый из нас может позволить указать 26 min API в своем проекте. Библиотеки, поддерживающие и Android и JVM, не могут позволить себе использовать API, представленный нам 5 лет назад!

И даже несмотря на то, что саппорт Java 8 теперь является частью D8, каждый разработчик все равно должен явно указывать source и target compatibility на Java 8. Если вы пишете собственные библиотеки, то можете усилить эту тенденцию, выкладывая библиотеки, которые используют Java 8 байткод (даже если вы не используете новые фичи языка).

Над D8 ведется очень много работ, поэтому, кажется, в будущем с поддержкой фичей языка все будет ок. Даже если вы пишете только на Kotlin, очень важно заставлять команду разработки Android поддерживать все новые версии Java, улучшать байткод и новые API.

Этот пост — письменная версия моего выступления Digging into D8 and R8.

Источник

версия building android error java

Я новичок в строительстве android OS.

Я использую Ubuntu 14.04 LTS и JDK 8.

В envsetup.sh дает :

И я пытаюсь скомпилировать с помощью make-J4

Я получаю дурацкую ошибку :

Поэтому я меняю JDK на версию 7, но получаю ту же ошибку, но перевернутую :

Так что же такое ПБ ?

Как я могу ее решить ?

Спасибо за вашу помощь

2 ответа

Я пытаюсь скомпилировать fastboot для Android с помощью этого руководства , но получаю следующую ошибку: ************************************************************ You are attempting to build with the incorrect version of java. Your version is: java version 1.6.0_23. The correct version is: Java.

Читайте также:  Управление умным домом андроид

Я столкнулся с той же ошибкой ERROR. API версия не найдена в установленном Android SDK: 1.6. Но при добавлении этого блока в build.yml моего приложения rhodes: android: версия: 4.2 эмулятор: rhoAndroid Теперь я получаю это error:ERROR. Неправильная версия Android API: 4 грабли run:android.

Вы запросили сборку OpenJDK 8, но ваша версия java версия «1.7.x» Java(TM) SE Среда выполнения (сборка 1.7.x) Java HotSpot(TM)

Вам нужно Java 7, как указано в сообщении об ошибке. Скачайте OpenJdk 7 и установите его. Тогда вам нужно

На обоих из них выберите 1.7 или 7 для версии. Google и XDA уже рассмотрели это. Java-8 будет использоваться для Android N в дальнейшем, но для KK и MM вам нужно 7.

Я знаю, что эта тема старая, но сегодня я работаю с Android 6, поэтому я прошел через ту же проблему. Для меня это помогло:

Если вы хотите принудительно использовать Java 8, вы можете использовать это:

Затем сделайте еще раз:

Но, как следует из названия, он экспериментальный, поэтому не рекомендуется.

Для меня (Ubuntu 18.04.1) я должен был скачать Java 7 ( https://jdk.java.net/java-se-ri/7 ) и:

Вышеизложенное создаст /usr/lib/jvm/java-se-7u75-ri и установит его как принадлежащее пользователю и группе root .

: [. ] добавьте правильный JDK в начало вашего пути или удалите проблемный JDK.

Мне также пришлось залатать build/core/main.mk :

Обратите внимание, что это было grep ‘ ing для ‘^java .*[ «]1\.7[\. «$$]’ , и я изменил его на ‘^openjdk .*[ «]1\.7[\. «$$]’ .

После этого я выдал m , чтобы сделать еще раз.

Надеюсь, что это может помочь кому-то еще там. 🙂

Похожие вопросы:

Я пытаюсь построить исходники android и получаю странную ошибку. Я выбрал java, используя $ sudo update-alternatives —config java $ sudo update-alternatives —config javac AND $ update-alternatives.

С новым кандидатом на выпуск Android Studio 1.0 1.0 я получаю эту ошибку: Неподдерживаемая версия major.minor 51.0 Я использую jdk1.7.0_60.jdk Я пробовал это сделать, но безуспешно.

Я постоянно задаюсь вопросом, как версия Java, используемая для разработки Android, соотносится с версией Java SE. Например, сегодня я читал о Type Inference and Generic Methods, который является.

Я пытаюсь скомпилировать fastboot для Android с помощью этого руководства , но получаю следующую ошибку: ************************************************************ You are attempting to build with.

Я столкнулся с той же ошибкой ERROR. API версия не найдена в установленном Android SDK: 1.6. Но при добавлении этого блока в build.yml моего приложения rhodes: android: версия: 4.2 эмулятор.

Ваш Android SDK устарел или в нем отсутствуют шаблоны. Пожалуйста, убедитесь, что вы используете SDK версии 22 или более поздней. На машине, которую я использую, раньше не было Eclipse, но пришлось.

Есть ли версия nanosleep C, которая существует в Java/Android? я пытаюсь отправить некоторые команды из Android по последовательному каналу, и я хотел бы спать в течение очень небольшого количества.

У меня ошибка в Unity при создании игры на телефон Android. Это 5-й день поиска решения. Я искал подобные проблемы по всему интернету, большинство из них были связаны с сервисами google play, но у.

Я новичок в кросс-уолке. Я пытался настроить android в Ubuntu, я сделал настройку здесь . Но у меня есть проблема, как bellow.please кто-нибудь решить эту проблему? >$ crosswalk-app check android.

Я обновил свой R до версии 3.5.0 из-за всех хороших вещей, которые я слышал о нем. На самом деле все OK до сих пор. Проблема в том, что я пытаюсь загрузить свою панель мониторинга shiny в.

Источник

Оцените статью