Jni 使用总结
本文是原创,转载请声明。
前言
jni简介:JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。
jni设计的目的:标准的java类库可能不支持你的程序所需的特性。你已经有了一个用其他语言写成的库或程序,而你希望在java程序中使用它。需要用底层语言实现才能实现的功能。
java调用c
1.加载c库和调用函数
public class JniDemo{
static{
//1.加载C库
System.loadLibrary("native");
}
//2.声明函数来自于C库
public native static void hello();
public native static int hello1(String str,int []a);
public static void main(String[] args) {
//3.调用
hello();
}
}
java程序中使用System.loadLibrary
加载c库,括号native
(根据实际修改)是so库的名字。
在使用函数的时候需要先使用native
(固定的)声明函数,如果没有使用static
,需要实例化对象之后才能使用。
2.c程序做好接口
a.包含jni头文件
#include <jni.h>
b.当java程序加载C库的时候会调用c程序的JNI_OnLoad
函数
static const JNINativeMethod methods[] = {
{"hello", "()V", (void *)c_hello},
};
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;
//获取运行环境
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
}
//查找对应的类
cls = (*env)->FindClass(env, "JniDemo");
if (cls == NULL) {
return JNI_ERR;
}
//建立联系
if((*env)->RegisterNatives(env,cls,methods,1)<0) //环境,类,结构体,函数个数
{
return JNI_ERR;
}
return JNI_VERSION_1_4;
}
在JNI_OnLoad
函数中
第一步获取运行环境
;
第二步查找对应的类
,括号中的类就是我们上文我们提到加载C库的类;
第三步建立联系
,重点对参数中的后面两个进行介绍:最后一个参数是指建立联系的方法个数,需要与methods
对应上。
JNINativeMethod结构体介绍 :
methods
是JNINativeMethod
结构体的数组,当只有一个函数需要建立连接时,需要有多个JNINativeMethod
结构体;结构体中第一参数名字
(自己定义),在java程序声明的时候使用;第三个参数就是我们的函数名
;
static const JNINativeMethod methods[] = {
{"hello", "()V", (void *)c_hello},
//... 多一个函数增加多一行类似的
};
第二个参数JNI字段描述符
用工具生成:
javac JNIDemo.java
javah -jni JniDemo
会生成一个JNIDemo.h
文件,类似:Signature
参数就是我们需要的。
/*
* Class: JniDemo
* Method: hello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniDemo_hello
(JNIEnv *, jclass);
c.需要调用的函数
void c_hello(JNIEnv *env, jclass cls)
{
printf("%s\n","hello");
}
c函数除了自己的参数外,需要增加多两个参数:JNIEnv *env
和jclass cls
d.参数传递
当java调用c函数,很多时候都是需要传入参数,返回参数的。
例子:
java程序:
public native static String hello(String str);
c程序:
jstring c_hello(JNIEnv *env, jclass cls,jstring str)
c程序和java中的参数类型不是一致的需要转换,中间使用的jni的类型进行转换,下图是转换表。
基本类型:(只列举几个)
java类型 | jni本地类型 | c语言 |
---|---|---|
int | jint | int(32bit) |
boolean | jboolean | unsigned char(8bit) |
float | jfloat | float(32bit) |
char | jchar | unsigned short(16bit) |
byte | jbyte | char(8bit) |
其他类型:String
和 Array
java类型 | jni本地类型 | c语言 |
---|---|---|
String | jString | 无 |
int[] | jintArray | int[] |
String:native语言中没有String类型的数据
jstring c_hello(JNIEnv *env, jclass cls,jstring str)
{
const jbyte *cstr;
cstr = (*env)->GetStringUTFChars(env, str, NULL);//有分配内存的操作
if (cstr == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("get String from java %s\n", cstr);
(*env)->ReleaseStringUTFChars(env, str, cstr);//释放空间
return (*env)->NewStringUTF(env, "return form c");
}
Java程序传进来String被转成jstring,因为C程序没有对应的类型只能转成字符类型。
String >> jstring >> jbyte(char)使用GetStringUTFChars
函数;
使用完成后记得调用ReleaseStringUTFChars
对内存进行释放。
char >> jstring >> String 使用NewStringUTF
创建一个jstring类型的数据。
Array:
具体见代码
jintArray c_hello(JNIEnv *env, jclass cls,jintArray arr)
{
jint *carr;
jint *oarr;
jint i,arr_lenght = 0;
jintArray rarr;
carr = (*env)->GetIntArrayElements(env, arr, NULL); //获取数组
if (carr == NULL) {
printf("cat not get the IntArray\n");
return 0;
}
arr_lenght = (*env)->GetArrayLength(env,arr); //获取数组长度
printf("the data from java to c :\n");
for(i=0;i<arr_lenght;i++)
{
printf("%d\n",carr[arr_lenght-1-i]);
}
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);//释放数组
//创建IntArray进行返回
rarr = (*env)->NewIntArray(env,5); //环境,长度
if(rarr==NULL)
{
printf("cat not create IntArray\n");
return 0;
}
oarr = malloc(sizeof(jint)*5);
if(oarr==NULL)
{
printf("cat not malloc the \n");
return 0;
}
for(i=0;i<5;i++)
{
oarr[i]=i;
}
//设置jintArray参数的内容
(*env)->SetIntArrayRegion(env,rarr,0,5,oarr);
free(oarr);
return rarr;
}
3.编译执行
编译Java文件:javac JniDemo.java
编译so库:
gcc -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -fPIC -shared -o libnative.so native.c
-I 指定头文件目录
-o 生成指定文件
设置环境变量:export LD_LIBRARY_PATH=.
该环境变量主要用于指定查找共享库(动态链接库)时除了默认路径之外的其他路径
运行:java JNIDemo
总结
对学的进行总结,有错误的地方欢迎指正。–by manjia