1.JNI技术框架
首先,Java代码是运行在JVM上的class集合,是通过JVM解释执行,这也是Java能够跨平台的根本原因;其次,在某些与硬件或者操作系统平台相关的Java软件中,需要在Java代码中调用使用C、C++等编写的本地应用或者库文件;最后,JNI(Java Native Interface)是一个能使Java代码调用或被这些本地应用调用的程序框架。
JNI技术的目的是为了实现Java语言与其他编程语言(汇编、C、C++等)的交互。那么在什么时候需要用到这项技术呢?(1)软件不能完全采用Java语言编程时,例如对Java标准类文件不能直接操作的与平台、硬件等编程时;(2)软件对时间或者性能要求较高时,需要用到底层语言实现对对象的驱动(本地代码要比JVM的执行效率更高);(3)用其他编程语言已经实现了功能,在Java代码中直接调用已经生产的库文件(如动态链接库)。
JNI技术的实现原理如图1所示。首先通过在Java代码加载库文件,调用库文件实现的根方法(stubmethod);然后通过JNI加载动态库文件(Windows下为.dll文件,Linux下为.so文件),调用本地方法(nativemethod);最后在本地文件中实现功能,返回处理值。
图1 JNI调用原理
在Java中实现JNI一般有如下几个步骤:
(1)在Java代码中使用System.loadLibrary()加载库文件,并且声明Native方法,编译成class文件。
(2)使用javah将上步中生产的class文件生产头文件。使用格式如下:
Javah –jni xxx(xxx为class的包名+class名)
javah统一了头文件中的函数名和动态库中函数的实现之间的对应关系。
(3)采用其他编程语言(C、C++、Object-C)实现上述头文件中的函数,生产动态链接库给Java使用。
2.Android串口通信实现
在Android应用程序中,需要实现与外设之间的串口通信时,就需要用到以上的JNI技术。由于在Android的系统类库中并不存在操作串口的API,因此,需要自定义串口实现。
首先,在Java代码中加载库文件并且声明本地方法。
// JNI
private native static FileDescriptoropen(String path, int baudrate, intflags);
public native void close();
static {
System.loadLibrary("serial_port");
}
然后,使用javah生成头文件。
/* DO NOT EDIT THIS FILE - it is machinegenerated */
#include <jni.h>
/* Header for classandroid_serialport_api_SerialPort */
#ifndef _Included_android_serialport_api_SerialPort
#define _Included_android_serialport_api_SerialPort
#ifdef __cplusplus
extern "C" {
#endif
/*
*Class: android_serialport_api_SerialPort
*Method: open
*Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
*/
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
(JNIEnv *, jclass, jstring, jint, jint);
/*
*Class: android_serialport_api_SerialPort
*Method: close
*Signature: ()V
*/
JNIEXPORT voidJNICALL Java_android_serialport_SerialPort_close
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
最后,使用C语言实现上述SerialPort.h文件,并且生成.so文件(这一步需要在Linux中完成),然后把此.so文件复制到Android应用libs目录下(文件名为:libserial_port.so)即可。
具体代码可参见Google开源项目https://code.google.com/p/android-serialport-api/source/browse/trunk/android-serialport-api/project/jni/SerialPort.c