JNI使用心得

这几天在做一个ubuntu下使用JNI调用c程序的小程序,因为之前对JNI不太了解,其间遇到了不少问题,经过我和同学反复的排查才得以解决。下面总结在linux下使用JNI的一些心得(我用的开发工具是MyEclipse)。

java类TErasureCode主要用来加载.so的c程序,其中方法Encode和Decode与c程序中的编码与解码函数对应,TErasureCode.java代码如下:

package com.ping.ErasureCode;  //源码所在包

public class TErasureCode {
 static {
  System.loadLibrary("ErasureCode");  //载入ErasureCode.so
 }
 public native void Encode(String file);  //本地编码函数,与ErasureCode.so中的编码函数对应
 public native void Decode(String file);  //本地解码函数,与ErasureCode.so中的解码函数对应
}

将上面的TErasureCode.java编译成.h文件,这里要小心,在linux MyEclipse下我们应该将当前目录cd到工程的src下,使用"javah -jni com.ping.ErasureCode.TErasureCode"指令,使编译出的.h文件名为“com_ping_ErasureCode_TErasureCode.h”,这么做都是因为在MyEclipse下我们将源码都放在了com.ping.ErasureCode包下,否则会提示'UnsatisfiedLinkError"错误。注意:包名中的点“.”会变为下划线"_"。编译好的TErasureCode.h不要去改动它,因为是机器自动生成的,改动后可能会出现连接失败错误,TErasureCode.h的源码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ping_ErasureCode_TErasureCode */

#ifndef _Included_com_ping_ErasureCode_TErasureCode
#define _Included_com_ping_ErasureCode_TErasureCode
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ping_ErasureCode_TErasureCode
 * Method:    Encode
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ping_ErasureCode_TErasureCode_Encode  //编码,点变为了下划线
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_ping_ErasureCode_TErasureCode
 * Method:    Decode
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ping_ErasureCode_TErasureCode_Decode  //解码,点变为了下划线
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

以上标记红色的函数名要与你的C源码中的函数完全对应,一定不能有差错。我的C程序的部分源码如下:

#include <iostream>
#include "com_ping_ErasureCode_TErasureCode.h"  //包含上面编译出的头文件
#include <sys/time.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
using namespace std;

............................

int encoder (char *filename){..............}

int decoder (char *filename){..............}

...........................

char* jstringToString(JNIEnv *env, jstring jstr) //将jstring转换为string的方法
{
 char* temp = NULL;

 jclass clsstring = env->FindClass("java/lang/String");
 jstring strencode = env->NewStringUTF("utf-8"); //使用utf-8可以处理中文,但用gb2312却不行,不知为什么
 jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
 jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0)
    {
                 temp = (char*)malloc(alen + 1);
                 memcpy(temp, ba, alen);
                 temp[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);

 return temp;
}


JNIEXPORT void JNICALL Java_com_ping_ErasureCode_TErasureCode_Encode(JNIEnv *env, jobject obj, jstring file)//与.h中相同的函数名
{
 char* file1 = NULL;
 file1 = jstringToString(env, file);

 encoder(file1);//调用具体编码函数
}

JNIEXPORT void JNICALL Java_com_ping_ErasureCode_TErasureCode_Decode(JNIEnv *env, jobject obj, jstring file)//与.h中相同的函数名
{
 char* file1 = NULL;
 file1 = jstringToString(env, file);//调用具体解码函数

 decoder(file1);
}

使用下面的指令将ErasureCode.c编译成ErasureCode.so文件:

g++ -I /usr/java/jdk1.6.0_45/include -I /usr/java/jdk1.6.0_45/include/linux -fPIC -c ErasureCode.c
g++ -shared -W1,-soname,libErasureCode.so.1 -o libErasureCode.so.1.0 ErasureCode.o
cp libErasureCode.so.1.0 libErasureCode.so

将生成的.so文件放入系统JDK的安装目录下,具体路径为:/..../jdk1.6.0_45/jre/lib/i386

最后MyEclipse工程中com.ping.ErasureCode包只需要如下的文件:

TErasureCode.java, TErasureCode.class, com_ping_ErasureCode_TErasureCode.h

测试时直接在包中新建一个测试类,new一个TErasureCode对象,在调用其native方法即可,我的测试类如下:

package com.ping.ErasureCode;

public class testEn {
 public static void main(String[] args) {
  TErasureCode tEn = new TErasureCode();
  tEn.Encode(args[0]);//调用native方法
 }
}

至此完成!过程中C代码一定要保证没有错误,其间就因为C代码出现数组越界、长度不够或内存泄露的问题,产生不少错误,拖延了项目进度。此外,特别注意的就是源码在包中时,一定要注意包也要加入到.h文件名中,否则MyEclipse不知道从哪里找都相关文件(通常我们认为源码在一个包下,系统就会自动找到同一包中的其他文件,但.h文件好像不是这样的)。

希望能给初用JNI的朋友提供一些帮助,也希望我们能共同探讨更深入的知识。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值