翻译:Ross.Zeng 校对:
Dalvik 虚拟机与 Android 下的 Java¶
简而言之,Dalvik就是安卓的java虚拟机。它允许安卓运行由java编写的应用和系统组件生成的字节码,并向系统提供所需的钩子和环境接口,包含原生链接库与原生用户空间。关于Dalvik和安卓品牌可以扯很远。但为了深入研究,首先需要掌握一些java基础。
为了不让你们再上一节关于java语言与其起源的历史课,就直接说java是90年代初期由James Gosling在Sun公司创造的。java一经面试就迅速流行起来,在安卓出现就已经足够完善。对于开发者而言,有两点需要牢牢记住:java与C/C++这些传统的编程语言不同,我们常说的“Java”由组件构成。
从设计上看,Java是一种解释性的语言。不同与C/C++,当代码被编译器编译后,变为二进制汇编指令被解释器执行。这些与CPU架构独立的字节码解释器能够在运行时执行字节码,这就是我们常说的“虚拟机”。如此的操作方式与Java语义,使Java中拥有了很多传统语言中不存在的特性,如反射和匿名类。另外,Java也不需要像C/C++那样一直跟踪分配的对象。实际上,它不需要关心任何未使用的对象,因为它自身的垃圾回收机制可以保证一旦没有活动的代码引用后就会将对象销毁。
从使用角度看,Java实际由这些东西组成:Java编译器,Java解释器——通常被成为Java虚拟机(JVM),以及Java链接库,通常被称为Java开发套件(JDK)由甲骨文提供。安卓实际上在编译时使用了JDK,但并没有用到JVM或其链接库。它实际用了Apache的Harmony项目。
据开发者Dan Bornstein说,Dalvik是一个为嵌入式系统定制的JVM。它的目标是那些CPU比较慢,内存空间少,跑操作系统不需要交换空间,且使用电池供电的设备。
相比JVM处理的是.class文件,Dalvk更加喜欢.dex。.dex实际由安卓通过dx组件通过Java编译器对.class类处理生成。一个未经压缩的.dex文件要比原生的.jar文件小50%。另一个有趣的事情是,Dalvik基于寄存器,而JVM基于堆栈。除非你是虚拟化或体系结构的学生,亦或者对此非常感兴趣,否则以上内容没有太大意义。
Davlik有一个特点非常值得重点关注,从2010年开始,它为ARM加入了即时编译器。这意味着Dalvik将应用的字节码转化为二进制汇编指令并可以运行在原生的目标CPU上,再不是在虚拟机中临时解释每条指令。如此变换可以为今后的特性使用。因此,应用程序会在第一次载入时多花些时间,但是一旦已经被载入过,之后就会更加迅速。唯一需要注意的是,JIT不能被ARM外的架构所使用。所以总而言之,目前运行安卓系统最快的架构就是ARM。
作为一名嵌入式开发人员,让Dalvik跑起来不需要额外做什么特别的事情。Dalvik是以架构独立的方式实现的。早前有报道Dalvik中存在一些字节序的问题,然而早就被解决了。
Java原生接口(JNI)¶
尽管Java强大并且易用,但它总不能自己单打独斗。并且在码代码时还需要调用一些其他语言的接口。尤其在安卓这种嵌入式系统中,总要经常和底层功能打打交道。为了实现这个需求,Java原生接口机制出现了。类似于.NET/C#中的pinvoke,它实际上是C/C++这类外部语言的调用入口。
应用程序开发人员有时使用JNI调用一些他们编译的NDK原生代码,就像使用SDK编译Java。其实在AOSP内部就大量使用JNI来让Java编码的服务和组件与C/C++实现的底层功能对接。例如Java编写的系统服务,就使用JNI与对应的原生代码接口通信,从而将服务传达至相应的硬件设备。
允许Java通过JNI的方式与其他语言交互,很大部分的功能是由Dalvik提供的。回到上个章节的表2-3中,你会发现libnativehelper.so这个库就是由Dalvik提供,便于使用者调用JNI的。
在本书稍后部分,我们有机会使用JNI连接Java和C代码。目前阶段,请记住,JNI是安卓系统的核心部分,也是一个使用起来相对复杂的机制。特别要确保调用时正确地给出了语义与参数。
图 2-4. 系统服务