JNI是Java Native Interface的简写,一般译作Java本地接口.Java可以通过JNI调用C/C++库,这在安卓开发中无疑是非常方便的.因为Android内核是C语言写的,Android又对内核接口进一步封装,这层称为HAL层,一般是C++写的.而Android应用程序是Java写的,Java怎么操作硬件接口?这就是JNI层存在的作用.
在Java代码中通过JNI调用C函数的步骤如下.(参考
- 编写Java代码
- 编译Java代码
- 生成C语言头文件
- 编写C代码
- 生成C共享库
- 运行Java程序
第一步,编写Java代码
要调用C库函数,Java类中的方法必须用native修饰.一般在静态代码块中加载本地代码库,调用System.loadLibrary()函数.
class HelloJNI {
// native method
native void printHello();
native void printString(String str);
// load library
static {
System.loadLibrary("hellojni");//在Linux下指libhellojni.so
}
public static void main(String args[]) {
HelloJNI myJNI = new HelloJNI();
// call native method
myJNI.printHello();
myJNI.printString("Hello World from printString fun");
}
}
这里关于静态代码块,多说几句.
1.它是随着类的加载而执行,只执行一次,并优先于主函数.具体说,静态代码块是又类调用的,类调用时,先执行静态代码块,然后才执行主函数的.
2.静态代码块其实是给类初始化化的,而构造代码块是给对象初始化的.
3.静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别.
4. 一个类可以有多个静态代码块.
第二步,编译Java代码
javac HelloJNI.java
这一步很简单,不多做介绍.
第三步,生成C语言头文件
若想创建本地方法的映射C函数,必须先生成函数的原型,函数原型位于C/C++头文件中,Java提供了javah工具,用来生成包含函数原型的头文件.使用方法如下.
javah <包含以native关键字声明的方法的Java类名称>
来看看生成的HelloJNI.h中的内容.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: printHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printHello
(JNIEnv *, jobject);
/*
* Class: HelloJNI
* Method: printString
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printString
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
观察函数原型名称,可以发现函数名遵循一定的命名规则.JNI支持的函数命名形式为”Java/类名/地方法名”.了解这些命名规则,通过函数命名即可推断出JNI本地函数与哪个Java类的哪个本地方法相对应.比如Java_HelloJNI_printHello()函数原型,通过其名称,可以知道它与HelloJNI类中的printHello()方法对应.至于函数原型的参数,可以参考JNI相关的文档,在此不多做介绍.
JNI提供了一套与Java数据类型相对应的Java本地类型,使得本地语言可以使用Java数据类型,如下表所示.
Java类型 | Java本地类型 | 占用内存大小 |
---|---|---|
byte | jbyte | 1 |
short | jshort | 2 |
int | jint | 4 |
long | jlong | 8 |
float | jfloat | 4 |
double | jdouble | 8 |
char | jchar | 2 |
boolean | jboolean | 1 |
void | void |
第四步,编写C/C++代码
#include "HelloJNI.h"
#include <stdio.h>
/*
* Class: HelloJNI
* Method: printHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printHello(JNIEnv *env, jobject obj)
{
printf("Hrllo World!\n");
return ;
}
/*
* Class: HelloJNI$
* Method: printString
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printString(JNIEnv *env, jobject obj, jstring string)
{
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s!\n", str);
return ;
}
编译
gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -shared -fPIC -o libhellojni.so hellojin.c # 生产共享库
第五步,运行Java程序
运行
java HelloJNI
发现出错
Exception in thread “main” java.lang.UnsatisfiedLinkError: no hellojni in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1889) at java.lang.Runtime.loadLibrary0(Runtime.java:849) at java.lang.System.loadLibrary(System.java:1088) at HelloJNI.<clinit>(HelloJNI.java:8)
通知动态链接程序此共享文件的路径
export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH
再次运行.
Hello World!
Hello World from printString fun!