- Java 8
- Новое в Java 8
- default
- Predicate/Предикаты
- Function/Функции
- Supplier/Поставщики
- Consumer/Потребители
- Comparator/Компараторы
- Другое
- Дополнительные материалы
- How to use Java 8 features in Android Studio ?
- Conclusion
- Use Java 8 language features — Android Studio
- What is desugar ?
- How to add Java 8 language Feature support in your project ?
- Migrate to the default toolchain
- Supported Java 8 Language Features and APIs
- Как Java 8 поддерживается в Android
- Лямбды
- История десахаризации
- Преобразование исходников — Source Transformation
- Нативная поддержка лямбд
- Method References
- Default и static методы в интерфейсах
- Может, просто использовать Kotlin?
- Desugaring APIs
- Эпилог
Java 8
Наконец-то появилась поддержка Java 8. С чем его едят?
Android всегда запаздывала с поддержкой новых версий Java. Когда уже была Java 7, официально поддерживалась версия Java 6, хотя многие пользовались седьмой версией на свой страх и риск. В целом проблем не было, если не использовались некоторые специфические новинки. Затем поддержку седьмой версии подтвердили официально.
Та же история повторяется с версией Java 8.
Убедитесь, что у вас установлена Java 8. Если вы устанавливали Android Studio с нуля, начиная с версии 2.2, то по умолчанию студия устанавливает в своей папке jre специальную версию OpenJDK, которая является альтернативой JDK от Oracle. Вы можете использовать любые из этих версий, хотя рекомендуется использовать первый вариант.
В Android Studio 4.0 поддержка Java 8 расширилась.
В документации показан следующий способ. Создаём новый проект. В build.gradle проекта в секцию android добавляем следующие записи.
Также можно это сделать через настройки студии в меню File | Project Structure, используя раздел app. На вкладке Properties установите версии 1.8 для опций Source Compatibility и Target Compatibility.
Теперь вы можете использовать новые возможности, предоставляемые Java 8.
Поддержка Java 8 не является полной. Подробнее можно посмотреть на странице https://developer.android.com/studio/write/java8-support.html#supported_features.
Новое в Java 8
default
Методы интерфейсов по умолчанию. Java 8 позволяет вам добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово default.
Кроме абстрактного метода calculate() интерфейс Formula также определяет метод по умолчанию sqrt(). Классы, имплементирующие этот интерфейс, должны переопределить только абстрактный метод calculate(). Метод по умолчанию sqrt() будет доступен без переопределения.
Predicate/Предикаты
Предикаты (predicate) — это функции-коты, принимающие один аргумент, и возвращающие значение типа boolean. Интерфейс содержит различные методы по умолчанию, позволяющие строить сложные условия (and, or, negate).
Предикаты могут входить в состав метода в качестве параметра. Смотри пример метода ArrayList.removeIf().
Function/Функции
Функции принимают один аргумент и возвращают некоторый результат. Методы по умолчанию могут использоваться для построения цепочек вызовов (compose, andThen).
Supplier/Поставщики
Поставщики (suppliers) предоставляют результат заданного типа. В отличии от функций, поставщики не принимают аргументов.
Consumer/Потребители
Потребители (consumers) представляют собой операции, которые производятся с одним входным аргументом.
Comparator/Компараторы
Компараторы хорошо известны по предыдущим версиям Java. Java 8 добавляет в интерфейс различные методы по умолчанию.
Другое
Дополнительные материалы
Java 8+ APIs available through desugarin — список поддерживаемых методов из Java 8.
Источник
How to use Java 8 features in Android Studio ?
Android Studio 3.0 (by 25th July 2017) only has a preview (canary) version available. Like the previous versions (2.3.x) it supports all Java 7 features and some of the Java 8 features. These subset of Java 8 features supported, vary by platform version. During your Android application development, it is not mandatory that you use Java 8 features, but compiling using Java 8 is mandatory. And in the ongoing tutorial, we shall learn how to use Java 8 features in Android Studio that are builtin.
The Jack tool chain that has been introduced in Android Studio 2.1 and higher is depreciated. Now Android Studio 3.0 has a built in support for a subset of Java 8 features. So, if you have been using Jack toolchain, it is advised to disable Jack toolchain and use the default toolchain provided by Android Studio 3.0 + for an improved Java 8 builtin support.
To disable Jack toolchain from your Android Application, remove the jackOptions snippet from defaultConfig block in build.gradle file.
To start using Java 8 features in your Android Application, update Android plugin to 3.0.0-alpha1 or higher and add the following compileOptions to build.gradle file in your app folder.
Following are the Java 8 features supported by all sdk versions (you may keep any minSdkVersion) :
- Lambda expressions (Exception : All values captured by the lambda should be serializable.)
- Method References
- Type Annotations (Exception : ElementType.TYPE_USE and ElementType.TYPE_PARAMETER are supported for sdk version 24+)
- Default and Static interface methods
- Repeating annotations
And following are the Java 8 APIs that are supported with a minSdkVersion of 24 (or higher) :
- java.lang.annotation.Repeatable
- AnnotatedElement.getAnnotationsByType(Class)
- java.util.stream
- java.lang.FunctionalInterface
- java.lang.reflect.Method.isDefault()
- java.util.function
If you experience any issues with the latest Java 8 features, you may disable it by disabling Desugar in your gradle.properties file as shown in the following.
Conclusion
In this Android Tutorial, we have learnt a way for how to use Java 8 features in Android Application development.
Источник
Use Java 8 language features — Android Studio
May 2, 2017 · 3 min read
The Jack toolchain is deprecated, as per this announcement . You may continue to use it, or try the latest preview version of Android Studio (Android Studio 2.4 Preview 4 and later) to use improved support for Java 8 language features built into the default toolchain.
“For those of you who tried the Jack compiler, we now support the same set of Java 8 language features but with faster build speed,” said James Lau, product manager of Android Studio.
Android Studio now provides built-in support for using certain Java 8 language features and third-party libraries that use them.
As shown in figure, the default toolchain implements the new language features by performing bytecode transformations, called desugar, on the output of the javac compiler.
What is desugar ?
Lambda Expr e ssions are not completely Syntactic Sugar, meaning compiler doesn’t translate them into something which is already understood by JVM. An example of syntactic sugar in Java is enum. The compiler converts them into a class of final static constant variables.
For lambda, a straight forward approach could be: just desugar the new syntax into anonymous classes during compile time. Lambda syntax written by the developer is desugared into JVM level instructions generated during compilation.
How to add Java 8 language Feature support in your project ?
Add the following to your module’s build.gradle file to start using supported Java 8 language features.
Migrate to the default toolchain
If Android Studio detects that your project is using Jack, Retrolambda, or DexGuard, the IDE uses Java 8 support provided by those tools instead. However, compared to the default toolchain, those tools lack some functionality and support. Using the default toolchain also includes support for third-party libraries that use Java 8 language features, Instant Run, and tools that depend on intermediate .class files. So, you should migrate to default toolchain.
Migrate from Jack
To disable Jack and switch to the default toolchain, simply remove the jackOptions block from your module’s build.gradle file:
Migrate from Retrolambda
To migrate to the default toolchain, remove the Retrolambda dependency from your project-level build.gradle file:
And remove the Retrolambda plugin and retrolambda block from each module’s build.gradle file:
Disable Support for Java 8 Language Features
If you’re experiencing issues related to the new support for Java 8 language features, you can disable it by adding the following to your gradle.properties file:
Supported Java 8 Language Features and APIs
Android Studio does not support all Java 8 language features, but more are being added in future releases of the IDE. Depending on which minSdkVrsion you’re using, certain features and APIs are available now, as described on official android document .
If you like this article, please don’t forget to click ❤ to recommend it to others.
Follow me on Medium and Twitter . to get notified about my new articles and stories
Источник
Как 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 .
В итоге наш класс превратился в это:
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.
Источник