Java 7语言特性在Android中的应用

189

想知道有没有人在Android中尝试使用新的Java 7语言特性? 我知道Android会读取Java生成的字节码并将其转换为dex文件。所以我的问题是,它能理解Java 7的字节码吗?


10
或者,也许你可以使用Java 7的语言特性,但编译成Java 6字节码? - Tyler
2
Android Studio现在在创建新项目时会提醒您: “如果minSdkVersion小于19,则无法使用try-with-resources,但其他Java 7语言功能是可以的。” - IgorGanapolsky
1
是的,我知道 :) 我们的项目终于开始使用Java 7了。 - Daniel Ryan
8个回答

166
如果您正在使用Android Studio,则Java 7语言应自动启用,无需任何补丁。Try-with-resource需要API Level 19+,而NIO 2.0的内容则缺失。
如果您无法使用Java 7功能,请参阅@Nuno在如何编辑您的build.gradle方面的答案。
以下内容仅供历史参考。

Java 7的一小部分可以在Android中使用(注意:我只在4.1上进行了测试)。

首先,您不能使用Eclipse的ADT,因为它是硬编码的,只有Java编译器1.5和1.6才兼容。您可以重新编译ADT,但我发现除了重新编译整个Android外,没有简单的方法来做到这一点。

但您不需要使用Eclipse。例如,Android Studio 0.3.2, IntelliJ IDEA CE和其他基于javac的IDE支持编译到Android并且您可以通过以下方式设置兼容性,甚至高达Java 8:

Enabling Java 7 on IntelliJ

这只允许使用Java 7的语言特性,由于一半的改进也来自库,所以你几乎无法从中受益。您可以使用的功能是不依赖于库的功能:
  • 钻石操作符(<>
  • 字符串开关
  • 多重捕获(catch (Exc1 | Exc2 e)
  • 数字文字中的下划线(1_234_567
  • 二进制文字(0b1110111

而这些特性目前还不能使用:

  • try-with-resources语句-因为它需要不存在的接口“java.lang.AutoCloseable”(在4.4+中可以公开使用)
  • @SafeVarargs注释-因为“java.lang.SafeVarargs”不存在

... "yet" :) 事实证明,尽管Android的库面向1.6版本,但是Android源代码确实包含了像AutoCloseable这样的接口,而传统的接口如Closeable也继承自AutoCloseable(虽然缺少了SafeVarargs)。我们可以通过反射来确认它们的存在。它们之所以被隐藏,仅仅是因为Javadoc使用了@hide标签,这导致“android.jar”没有将它们包含在内。

已经有一个现有的问题 如何构建具有隐藏和内部API可用性的Android SDK? 关于如何获取这些方法。您只需要使用我们定制的“android.jar”替换当前平台的现有“android.jar”引用,那么许多Java 7 API将变得可用(该过程类似于Eclipse中的操作。检查项目结构→SDKs)。

除了AutoCloseable之外,以下Java 7库功能也被揭示:

  • 在ConcurrentModificationException、LinkageError和AssertionError中的异常链接构造函数
  • 原始类型的静态.compare()方法:Boolean.compare()、Byte.compare()、Short.compare()、Character.compare()、Integer.compare()、Long.compare()。
  • Currency: .getAvailableCurrencies(), .getDisplayName()(但不包括.getNumericCode())
  • BitSet: .previousSetBit(), .previousClearBit(), .valueOf(), .toLongArray(), .toByteArray()
  • Collections: .emptyEnumeration(), .emptyIterator(), .emptyListIterator()
  • AutoCloseable
  • Throwable: .addSuppressed(), .getSuppressed()和4个参数的构造函数
  • Character: .compare(), .isSurrogate(), .getName(), .highSurrogate(), .lowSurrogate(), .isBmpCodePoint()(但不包括.isAlphabetic()和.isIdeographic())
  • System: .lineSeparator()(未记录?)
  • java.lang.reflect.Modifier: .classModifiers(), .constructorModifiers(), .fieldModifiers(), .interfaceModifiers(), .methodModifiers()
  • NetworkInterface: .getIndex(), .getByIndex()
  • InetSocketAddress: .getHostString()
  • InetAddress: .getLoopbackAddress()
  • Logger: .getGlobal()
  • ConcurrentLinkedDeque
  • AbstractQueuedSynchronizer: .hasQueuedPredecessors()
  • DeflaterOutputStream: 3个带有"syncFlush"的构造函数。
  • Deflater: .NO_FLUSH, .SYNC_FLUSH, .FULL_FLUSH, .带有4个参数的deflate()

基本上就是这样了。特别地,NIO 2.0并不存在,而Arrays.asList仍然不是@SafeVarargs。


2
很好的回答。我希望在未来能够完全支持JVM层,nio2和其他好玩意肯定是一个好消息。 - S.D.
4
值得一提的是,在 Android 运行时环境中,AutoCloseable 接口直到 ICS(或者可能是 HoneyComb)版本才存在。因此,即使您使用了打了补丁的 android.jar,在 2.x 系统上仍会收到 NoClassDefFoundError 错误。 - Idolon
2
@deviant:这需要修改Dalvik VM,因为Java 8 lambda使用的是“invokedynamic”,而JVM针对Java 6不支持它。 - kennytm
2
你可能想要添加一个更新,指出从Android Studio 3.2开始,支持完全的语言级别7,以及如果你正在针对KitKat进行编译,也支持try-with-resources。 - JRaymond
4
现在可以在SDK 19(Android Kitkat)上使用"try with resources"。请参见http://tools.android.com/recent/androidstudio032released。 - Mohamed ElNakeep
显示剩余11条评论

70

编辑:此篇文章撰写时,最新版本为Android 9和Eclipse Indigo。事情已经发生了改变。

  • 实用回答

是的,我尝试过。但这不是一个很好的测试,因为兼容性仅限于级别6,并且没有办法(至少没有简单的方法)真正使用Java 7:

  • 首先,在没有安装Eclipse和Android的计算机上安装了JDK7:

这台机器上只安装了7

  • 然后,我安装了全新的Eclipse Indigo,并检查它实际上是否正在使用JDK 7(嗯,由于这是唯一的版本,并且由于这是我选择的版本,我会感到惊讶)

这个Eclipse只使用了7

  • 接着,我安装了最新版本的Android SDK(编辑:Honeycomb,API13,当此帖子撰写时)。它找到了我的JDK 7并正确安装了。ADT也是如此。

  • 但是,在尝试编译和运行Hello Word Android应用程序时,我有一个惊喜。兼容性设置为Java 6,没有办法强制它使用Java 7:

兼容性仅限于Java 6

  • 我尝试了一个非Android项目,一个普通的Java项目,并得到了解释。兼容性级别似乎受Eclipse限制(请参见以下图像底部的消息):

Eclipse将自己限制在6级兼容性

我已经让Hello World和其他更复杂的应用程序工作,它们使用了SQLiteListviewSensorCamera,但这只证明了Java 7的兼容性处理似乎已经很好地与Android配合工作。

那么,有人尝试过使用好老的Ant来绕过上述的Eclipse限制吗?

  • 理论回答

无论如何,SDK都是设计用于Java 5或6的,正如在这里所解释的那样。

我们可以通过Java 7来实现一些工作,但这只是“偶然”能够工作。 DEX的构建可能正常工作,也可能不正常工作,而一旦DEX构建完成,它也可能正常工作或不正常工作。 这是因为使用未经资格认证的JDK会带来不可预测的结果。

即使有人成功地在纯Java 7下构建了Android应用程序,这也不能说明JDK是合格的。将同样的过程应用于另一个应用程序可能会失败,或者生成的应用程序可能存在与该JDK的使用相关的错误。不推荐此做法。

对于从事Web应用程序开发的人来说,这与在仅为Java 4合格的应用服务器上(例如Weblogic 8)部署在Java 5或6下构建的Web应用程序是完全相同的。这可能会起作用,但这不是可以推荐用于其他目的的东西。


1
感谢您的详细审查。看起来好像您不能使用Java 7语言特性,但仍然可以将其用作Java 6。希望这很快会改变 :) - Daniel Ryan
这是使用Eclipse。通过Ant,这可能是可行的。我希望有人能够进行测试,而我责怪自己太懒,没有去做它 :) - Shlublu
是的,Varga,但我认为编译器版本限制不是来自Ant而是来自Eclipse。 - Shlublu
2
请注意,如果您正在尝试使用多个版本的Java,则提供的工具可能不兼容。我的意思是,如果您首先使用Java 6工具中的jarsigner对应用程序进行签名,然后稍后安装了Java 7并使用Java 7附带的jarsigner和之前相同的密钥库对应用程序的新版本进行签名,则签名将不匹配! - Timo

38

dalvikvm.com的引用:

dx是包含在Android SDK中的一个工具,它将普通Java编译器编译的Java类的Java Class文件转换为另一种类文件格式(即.dex格式)。

这意味着,.java源文件并不重要,只有.class字节码文件才是关键。

据我所知,在Java 7中,只有invokedynamic被添加到JVM字节码中,其余部分兼容Java 6。Java语言本身并不使用invokedynamic。其他新功能,比如使用Stringswitch语句或多个catch仅仅是语法糖,并不需要字节码更改。例如,多个catch只是为每个可能的异常复制catch块。

唯一的问题应该是Java 7中引入的新类在Android中缺失,比如AutoCloseable,因此我不确定是否可以使用try-with-resources功能(有人尝试过吗?)。

对此有什么评论吗?我是否遗漏了什么?


2
现在的问题是,我们如何配置Java 7源代码编译成Java 6类文件,尤其是在Eclipse中? - Randy Sugianto 'Yuku'
唯一剩下的问题是,你为什么还要费心呢? - Warpzit
@Warpzit 更大的问题应该是,为什么开发者不会因为所有这些混乱而感到烦恼呢? - Amit
@Amit 因为他已经意识到 Android 与 Java 不同,为了使用 Android,他必须使用提供的工具。 - Warpzit
2
@Warpzit 他唯一的问题是“安卓能理解Java 7吗?”无知从来不是解决方案/答案... - Amit

12
截至Android SDK v15版本,以及Eclipse 3.7.1版本,Java 7不支持用于Android开发。将源兼容性设置为1.7需要将生成的.class文件兼容性设置为1.7,这会导致Android编译器出现以下错误:

Android需要编译器兼容级别5.0或6.0。找到'1.7'。请使用Android工具>修复项目属性。


5

为了进一步解释@KennyTM的答案,如果你的目标是4.0.3及以上版本(minSdkVersion=15),你可以通过向目标SDK android.jar中添加一些类来使用隐藏的API。

一旦这样做,您就可以在任何Closeable上使用try-with-resources,并在自己的类中实现AutoCloseable。

我制作了一个zip文件,其中包含所有需要修改android.jar中的类的源代码和二进制文件,以使这些API可用。您只需要解压缩它并将二进制文件添加到您的
android-sdk/platforms/android-NN/android.jar中即可。

您可以从这里下载:http://db.tt/kLxAYWbr

值得注意的是,在过去的几个月中,Elliott Hughes已经向Android代码库提交了一些内容:完成了AutoCloseable的结尾, 添加了SafeVarargs, 取消隐藏了各种API, 修复了Throwable的受保护构造函数在dx中添加了对版本51类文件的支持。因此,终于有了一些进展。

编辑(2014年4月):

随着SDK 19的发布,无需使用补丁程序将android.jar与其他API一起使用。

在针对4.0.3及以上版本(minSdkVersion=15)的应用程序中,在Android Studio中使用try-with-resources的最佳方法是将以下compileOptions添加到您的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会抱怨此API级别无法使用try-with-resources,但我的经验是可以的。该项目将在4.0.3及以上版本的设备上构建和运行,没有任何问题。我已经在安装了500k+设备的应用程序中使用过,没有遇到任何问题。

Android Studio error

如果您想忽略此警告,请将以下内容添加到您的 lint.xml 文件中:

<issue id="NewApi">
    <ignore regexp="Try-with-resources requires API level 19"/>
</issue>

1
我觉得有趣的是,Android Studio代码警告说try-with-resources在API 13中是新的,我应该使用它。虽然我没有时间实际测试它是否正常工作。 - Daniel Ryan

1
为了在使用基于Android的Ant构建系统编译的代码中使用Java 7特性,只需将以下内容放入项目根目录下的custom_rules.xml文件中即可:

custom_rules.xml:

<project name="custom_android_rules">
    <property name="java.target" value="1.7" />
    <property name="java.source" value="1.7" />
</project>

1

1
我寻找了很长时间。为了通过筛选文章来减少人们的麻烦,您需要更改由Android提供的build.xml中的<property name="java.source" value="1.5" />行(而不是您项目中的那个!)。对我来说,它在/opt/android-sdk-update-manager/tools/ant/build.xml中。 - Mateusz Kowalczyk
不,你不需要这样做。你可以使用custom_rules.xml覆盖这些属性,参见我在这里的回答:https://dev59.com/Mmw05IYBdhLWcg3wmCwo#24608415 - Flow

0

有些人可能会对我发现的这个git项目感兴趣,它似乎可以在Android上运行Java 7。 https://github.com/yareally/Java7-on-Android

然而,如果我将其添加到我目前正在工作的项目中,风险太大了。因此,我会等待Google正式支持Java 7。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接