Funkcje językowe Java 7 z systemem Android

188

Zastanawiasz się, czy ktoś próbował użyć nowych funkcji języka Java 7 w systemie Android? Wiem, że Android odczytuje kod bajtowy wypluwany przez Javę i zmienia go w dex. Wydaje mi się, że moje pytanie brzmi: czy można zrozumieć kod bajtowy Java 7?

Daniel Ryan
źródło
10
Alternatywnie, możesz użyć funkcji języka Java 7, ale skompilować do kodu bajtowego Java 6?
MatrixFrog,
2
Android Studio wyświetli teraz powiadomienie podczas tworzenia nowego projektu: „Przy minSdkVersion mniejszym niż 19 nie można używać try-with-resources, ale inne funkcje języka Java 7 są w porządku”
IgorGanapolsky
1
Tak, wiem :) W końcu używamy Java 7 w naszym projekcie.
Daniel Ryan

Odpowiedzi:

165

Jeśli używasz Androida Studio , język Java 7 powinien zostać włączony automatycznie bez żadnych łatek. Try-with-resource wymaga interfejsu API na poziomie 19+ i brakuje NIO 2.0.

Jeśli nie możesz korzystać z funkcji Java 7, zobacz odpowiedź @Nuno na temat edytowania build.gradle.

Poniższe informacje dotyczą wyłącznie historii.


Niewielką część Java 7 można z pewnością używać w Androidzie (uwaga: testowałem tylko na 4.1).

Po pierwsze, nie można było użyć narzędzia ADT Eclipse, ponieważ jest na stałe zakodowane, że tylko kompilator Java 1.5 i 1.6 jest zgodny. Możesz przekompilować ADT, ale uważam, że nie ma prostego sposobu na zrobienie tego oprócz ponownej kompilacji całego Androida.

Ale nie musisz używać Eclipse. Na przykład Android Studio 0.3.2 , IntelliJ IDEA CE i inne IDE oparte na Javie obsługują kompilację do Androida, a zgodność można ustawić nawet do Java 8 za pomocą:

  • Plik → Struktura projektu → Moduły → (wybierz moduł w 2. okienku) → Poziom języka → (wybierz „7.0 - Diamenty, ARM, wielozadaniowość itp.”)

Włączanie Java 7 na IntelliJ

Pozwala to tylko na funkcje języka Java 7 i nie można z niczego skorzystać, ponieważ połowa ulepszeń pochodzi również z biblioteki. Można użyć funkcji, które nie zależą od biblioteki:

  • Operator diamentowy ( <>)
  • Przełącznik strunowy
  • Wielokrotne połowy ( catch (Exc1 | Exc2 e))
  • Podkreślenie w literałach liczbowych ( 1_234_567)
  • Literały binarne ( 0b1110111)

Z tych funkcji nie można jeszcze korzystać :

  • Instrukcja try-with-resources - ponieważ wymaga nieistniejącego interfejsu „java.lang.AutoCloseable” (można tego użyć publicznie w wersji 4.4+)
  • Adnotacja @SafeVarargs - ponieważ „java.lang.SafeVarargs” nie istnieje

... „jeszcze” :) Okazuje się, że chociaż biblioteka Androida jest ukierunkowana na wersję 1.6, źródło Androida zawiera interfejsy takie jak AutoCloseable, a tradycyjne interfejsy takie jak Closeable dziedziczą po AutoCloseable (choć SafeVarargs naprawdę brakuje). Możemy potwierdzić jego istnienie poprzez refleksję. Są one ukryte po prostu dlatego, że Javadoc ma @hideznacznik, co spowodowało, że plik „android.jar” ich nie zawiera.

Istnieje już istniejące pytanie Jak zbudować zestaw Android SDK z dostępnymi ukrytymi i wewnętrznymi interfejsami API? jak odzyskać te metody. Wystarczy zastąpić istniejące odwołanie „android.jar” bieżącej platformy naszym dostosowanym, a następnie dostępnych będzie wiele interfejsów API Java 7 (procedura jest podobna do tej w Eclipse. Sprawdź Strukturę Projektu → SDK.)

Oprócz AutoCloseable ujawniono (tylko) następujące funkcje biblioteki Java 7 :

  • Konstruktorzy łączenia wyjątków w ConcurrentModificationException, LinkageError i AssertionError
  • Statyczne metody .compare () dla operacji pierwotnych: Boolean.compare (), Byte.compare (), Short.compare (), Character.compare (), Integer.compare (), Long.compare ().
  • Waluta : .getAvailableCurrencies (), .getDisplayName () (ale bez .getNumericCode ())
  • BitSet : .previousSetBit (), .previousClearBit (), .valueOf (), .toLongArray (), .toByteArray ()
  • Kolekcje : .emptyEnumeration (), .emptyIterator (), .emptyListIterator ()
  • AutoCloseable
  • Throwable : .addSuppressed (), .getSuppressed () i 4-argumentowy konstruktor
  • Znak : .compare (), .isSurrogate (), .getName (), .highSurrogate (), .lowSurrogate (), .isBmpCodePoint () (ale bez .isAlphabetic () i .isIdeographic ())
  • System: .lineSeparator () (nieudokumentowany?)
  • java.lang.reflect.Modifier : .classModifiers (), .constructorModifiers (), .fieldModifiers (), .interfaceModifiers (), .methodModifiers ()
  • NetworkInterface : .getIndex (), .getByIndex ()
  • InetSocketAddress : .getHostString ()
  • InetAddress : .getLoopbackAddress ()
  • Rejestrator : .getGlobal ()
  • ConcurrentLinkedDeque
  • AbstractQueuedSynchronizer : .hasQueuedPredecessors ()
  • DeflaterOutputStream : 3 konstruktory z „syncFlush”.
  • Deflater : .NO_FLUSH, .SYNC_FLUSH, .FULL_FLUSH, .deflate () z 4 argumentami

To w zasadzie wszystko. W szczególności NIO 2.0 nie istnieje, a Arrays.asList nadal nie jest @SafeVarargs.

kennytm
źródło
2
Świetna odpowiedź. Mam nadzieję, że pełne wsparcie na poziomie Jvm nastąpi wkrótce, nio2a inne gadżety z pewnością będą dobrą wiadomością.
SD
4
Warto wspomnieć, że AutoCloseableinterfejs nie istnieje w środowisku uruchomieniowym Androida do czasu ICS (a może do HoneyComb). Więc nawet jeśli użyjesz załatanego pliku android.jar, otrzymasz go NoClassDefFoundErrorw systemie 2.x.
Idolon,
2
@deviant: Wymaga to modyfikacji maszyny wirtualnej Dalvik, ponieważ Java 8 używa lambda, invokedynamicktóra nie jest obsługiwana przez JVM atakujący Javę 6.
kennytm,
2
Możesz chcieć dodać aktualizację, która od wersji Android studio 3.2, poziom językowy 7 jest w pełni obsługiwany, podobnie jak try-with-resources, jeśli kompilujesz z KitKat
JRaymond
4
spróbuj z zasobami można teraz używać w SDK 19 (Android Kitkat). patrz tools.android.com/recent/androidstudio032released
Mohamed El-Nakib
70

EDYCJA: W chwili pisania tego tekstu najnowszą wersją był Android 9 i Eclipse Indigo. Od tego czasu coś się zmieniło.

  • Praktyczna odpowiedź

Tak, próbowałem. Ale to nie jest świetny test, ponieważ zgodność była ograniczona do poziomu 6 bez żadnego sposobu (przynajmniej nie ma prostego sposobu), aby naprawdę korzystać z java 7:

  • Najpierw zainstalowałem JDK7 na komputerze, na którym nie było zainstalowanego innego JDK - Eclipse i Android też nie są zainstalowane:

7 jest jedynym zainstalowanym na tym komputerze

  • Potem zainstalowałem zupełnie nowy Eclipse Indigo i sprawdziłem, że faktycznie używa JDK 7 (cóż, ponieważ jest to jedyny i ponieważ to ten, który wybrałem, byłbym zaskoczony)

7 jest jedynym używanym w tym Eclipse

  • Następnie zainstalowałem najnowszą wersję zestawu Android SDK (EDIT: Honeycomb, API13, w momencie pisania tego postu). Znaleziono mój JDK 7 i został poprawnie zainstalowany. To samo dotyczy ADT.

  • Ale miałem niespodziankę, próbując skompilować i uruchomić aplikację Hello Word na Androida. Kompatybilność została ustawiona na Javę 6 bez możliwości wymuszenia jej na Javie 7:

Kompatybilność jest ograniczona do Java 6

  • Próbowałem z projektem innym niż Android, zwykłym Java, i miałem wyjaśnienie. Poziom zgodności wydaje się być ograniczony przez Eclipse (patrz komunikat na dole następującego obrazu):

Eclipse ogranicza się do kompatybilności na poziomie 6

Więc musiałem Hello World pracy, a także inne aplikacje, bardziej skomplikowane i za pomocą SQLite, Listview, Sensori Camera, ale to tylko dowodzi, że obsługa Java 7 zgodności wydaje się być dobrze zrobione i działa z systemem Android.

Czy ktoś próbował ze starą dobrą mrówką ominąć ograniczenie Eclipse widoczne powyżej?

  • Odpowiedź teoretyczna

W każdym razie SDK jest zaprojektowany do pracy z Javą 5 lub 6, jak wyjaśniono tutaj .

Możemy mieć coś do pracy z Javą 7, ale działałoby to „przez przypadek”. Budowanie DEX może działać poprawnie lub nie, a po zbudowaniu DEX może działać lub nie. Jest tak, ponieważ użycie niekwalifikowanego pakietu JDK z definicji daje nieprzewidywalne wyniki.

Nawet jeśli ktoś z powodzeniem zbudował aplikację na Androida na zwykłym Javie 7, nie kwalifikuje się do JDK. Ten sam proces zastosowany do innej aplikacji może się nie powieść lub aplikacja wynikowa może zawierać błędy związane z korzystaniem z tego JDK. Niepolecane.

Dla tych, którzy są zaangażowani w tworzenie aplikacji internetowych, jest to dokładnie to samo, co wdrażanie aplikacji WWW zbudowanej pod Javą 5 lub 6 na serwerze aplikacji kwalifikującym się tylko do Java 4 (powiedzmy na przykład Weblogic 8). Może to działać, ale nie jest to coś, co można polecić do innych celów niż próba.

Shlublu
źródło
1
Dziękuję za tę szczegółową recenzję. Wygląda na to, że nie możesz używać funkcji języka Java 7, ale nadal używasz Java 7 jako Java 6. Mam nadzieję, że wkrótce się to zmieni :)
Daniel Ryan
To jest z Eclipse. Z Antem jest to prawdopodobnie możliwe. Mam nadzieję, że ktoś zrobi test i obwiniam się za to, że jestem zbyt leniwy, aby to zrobić :)
Shlublu,
Tak, Varga, ale nie sądzę, że ograniczenie wersji kompilatora pochodzi od Anta, ale od Eclipse.
Shlublu,
2
Pamiętaj również, że jeśli bawisz się wieloma wersjami Javy, dostarczone narzędzia nie są kompatybilne. Chodzi mi o to, że jeśli najpierw podpisałeś aplikację za pomocą jarsignera z narzędzi Java 6, a następnie zainstalowałeś java 7 i podpisałeś nową wersję naszej aplikacji za pomocą jarsignera dostarczonego z java 7 i tego samego magazynu kluczy, co poprzednio, podpisy nie pasują !
Timo
38

Cytat z dalvikvm.com:

dx, zawarte w pakiecie Android SDK, przekształca pliki klas Java klas Java skompilowanych przez zwykły kompilator Java w inny format plików klas (format .dex)

Oznacza to, że plik źródłowy .java nie ma znaczenia, to tylko kod bajtowy klasy.

O ile mi wiadomo, do kodu bajtowego JVM w Javie 7 dodano tylko invokedynamic , reszta jest kompatybilna z Javą 6. Sam język Java nie używa invokedynamic . Inne nowe funkcje, takie jak instrukcja switch wykorzystująca String s lub multi- catch, to po prostu cukier syntetyczny i nie wymagały zmian kodu bajtowego. Na przykład, multi- połowowe tylko kopiuje haczyk -blok dla każdej możliwej wyjątku.

Jedynym problemem powinno być to, że w Androidzie brakuje nowych klas wprowadzonych w Javie 7, takich jak AutoCloseable , więc nie jestem pewien, czy możesz użyć funkcji try -with-resources (ktoś próbował?).

Jakieś komentarze na ten temat? Czy coś brakuje?

I ja
źródło
2
Problem polega na tym, jak skonfigurować tak, aby kody źródłowe Java 7 były kompilowane do plików klasy Java 6, szczególnie w środowisku Eclipse?
Randy Sugianto „Yuku”
Pozostaje tylko pytanie, dlaczego miałbyś w ogóle zawracać sobie głowę?
Warpzit,
@Warpzit większym pytaniem powinno być: Dlaczego programista nie zawracał sobie głowy tym całym zamieszaniem?
Amit
@Amit, ponieważ zdał sobie sprawę, że Android różni się od Java i aby pracować z Androidem, musi korzystać z oferowanych narzędzi.
Warpzit,
2
@Warpzit Jego jedyne pytanie brzmi: „ Czy Android może zrozumieć java 7? ” Niewiedza nigdy nie jest rozwiązaniem / odpowiedzią…
Amit
12

Począwszy od zestawu Android SDK v15, wraz z Eclipse 3.7.1, Java 7 nie jest obsługiwana w programowaniu Androida. Ustawienie zgodności źródła na 1.7 wymaga ustawienia wygenerowanej zgodności pliku .class na 1.7, co prowadzi do następującego błędu kompilatora Androida:

Android wymaga zgodności kompilatora na poziomie 5.0 lub 6.0. Zamiast tego znaleziono „1.7”. Proszę użyć Narzędzia Android> Napraw właściwości projektu.

Hosam Aly
źródło
5

Aby rozwinąć powyższą odpowiedź przez @KennyTM, jeśli kierujesz reklamy na wersję 4.0.3 i nowszą ( minSdkVersion = 15 ), możesz użyć ukrytych interfejsów API, dodając kilka klas do zestawu SDK celu.jpg.

Gdy to zrobisz, możesz użyć try-with-resources na dowolnym Closeable, a także zaimplementować AutoCloseable we własnych klasach.

Zrobiłem zip zawierający źródła i pliki binarne wszystkich klas, które musiały zostać zmodyfikowane w android.jar, aby te API były dostępne. Wystarczy go rozpakować i dodać pliki binarne do
Androida-SDK / Platform / Android-NN / Android.jar

Możesz go pobrać stąd: http://db.tt/kLxAYWbr

Warto również zauważyć, że w ciągu ostatnich kilku miesięcy Elliott Hughes dokonał kilku zmian w drzewie Androida: zakończył AutoCloseable , dodał SafeVarargs , niejawne różne API , naprawił chroniony konstruktor Throwable i dodał obsługę plików klasy 51 w dx . Tak więc wreszcie nastąpił pewien postęp.

Edycja (kwiecień 2014):

Wraz z wydaniem zestawu SDK 19 nie jest już konieczne łatanie pliku android.jar za pomocą dodatkowych interfejsów API.

Najlepszą metodą użycia try-with-resources w Android Studio dla aplikacji, która jest kierowana do wersji 4.0.3 i nowszych ( minSdkVersion = 15 ), jest dodanie compileOptionsdo build.gradle:

android {
    compileSdkVersion 19
    buildToolsVersion '19.0.3'

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 19
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

Android Studio będzie narzekać, że z tym poziomem interfejsu API nie można używać opcji wypróbowania zasobów, ale z mojego doświadczenia wynika, że ​​może. Projekt zostanie zbudowany i uruchomiony bez problemów na urządzeniach z wersją 4.0.3 i nowszą. Nie spotkałem się z tym z żadnymi problemami z aplikacją zainstalowaną na urządzeniach 500k +.

Błąd Android Studio

Aby zignorować to ostrzeżenie, dodaj następujące elementy do lint.xml:

<issue id="NewApi">
    <ignore regexp="Try-with-resources requires API level 19"/>
</issue>
Nuno Cruces
źródło
1
Uważam za interesujące, że ostrzeżenie o kodzie Android Studio mówi, że try-with-resources jest nowy w API 13 i powinienem go używać. Chociaż nie mam czasu na sprawdzenie, czy działa poprawnie.
Daniel Ryan
1

Wygląda na to, że uruchomienie tego z czystą mrówką jest trochę kłopotliwe.

Ale zadziałało to dla mnie: http://www.informit.com/articles/article.aspx?p=1966024

mako
źródło
1
Długo tego szukałem. Aby uniknąć kłopotów z ludźmi poprzez filtrowanie artykułu, musisz zmienić wiersz `<property name =" java.source "value =" 1.5 "/>` w pliku build.xml, który jest dostarczany przez Androida (nie ten w Twój projekt!). Dla mnie było to w /opt/android-sdk-update-manager/tools/ant/build.xml
Mateusz Kowalczyk
Nie ty nie. Możesz nadpisać te właściwości custom_rules.xml, patrz moja odpowiedź tutaj: stackoverflow.com/a/24608415/194894
Flow
1

Aby korzystać z funkcji Java 7 w kompilacji kodu przez system kompilacji oparty na mrówkach Androida, po prostu umieść w custom_rules.xmlkatalogu głównym projektów:

custom_rules.xml:

<project name="custom_android_rules">
    <property name="java.target" value="1.7" />
    <property name="java.source" value="1.7" />
</project>
Pływ
źródło
0

Niektórzy ludzie mogą być zainteresowani tym projektem git, który znalazłem, który pozwala na uruchomienie Java 7 na Androidzie. https://github.com/yareally/Java7-on-Android

Jednak zbyt duże ryzyko, jeśli dodam to w bieżącym projekcie, nad którym pracuję. Zaczekam więc, aż Google oficjalnie wesprze Javę 7.

Daniel Ryan
źródło