Jni 使用总结第一篇:java调用c库

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结构体介绍 :
methodsJNINativeMethod结构体的数组,当只有一个函数需要建立连接时,需要有多个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 *envjclass 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语言
intjintint(32bit)
booleanjbooleanunsigned char(8bit)
floatjfloatfloat(32bit)
charjcharunsigned short(16bit)
bytejbytechar(8bit)

其他类型:StringArray

java类型jni本地类型c语言
StringjString
int[]jintArrayint[]
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

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值