jni in linux

 JNI调用是JAVA访问本地代码的一个接口规范,在Windows平台下,本地代码以动态链接库(dll)的形式存在;而在LINUX平台下,本地代码则以共享库(so)的形式存在,特别要注意一点,Linux共享库的命名都存在一个共同点--以lib开头,我们在/usr/lib、 /usr/jre/lib/i386等目录都能找到类似的共享库。

一.系统环境
二.JNI的简要使用例子
三.JNI调用中考虑的问题
四.JNI中对中文数据的处理

一.系统环境
linux操作系统kernel2.6.9,安装j2sdk1.5.0_04


二.JNI的简要使用例子
下面是一个简单的例子实现打印一句话的功能,但是用的c的printf最终实现。一般提供给java的jni接口包括一个so文件(封装了c函数的实现)和一个java文件(需要调用path的

类)。
1. JNI的目的是使java方法中能够调用c实现的一些函数,比如以下的java类,就需要调用一个本地函数testjni,首先需要创建文件weiqiong.java,内容如下:
class TestJNI
{
    static
    {
        System.loadLibrary("testjni");//载入静态库,test函数在其中实现,在类型初始化前加载本地库,此处注意名称,本地库名应该为libtestjni.so,调用的SO

必须在系统属性的        java.libray.path 目录下(默认为/usr/jre/lib/i386)运行时可用-D选项指定,java -Djava.library.path=/root/workspace/lib
    }    
    public native void testjni(); //声明本地调用
    public void test(String input)
    {
        testjni(input);
    }
    public static void main(String args[])
    {
    TestJNI haha = new TestJNI();
    String input = "haha------------go into c++!";
    haha.test(input);
    }
}

2.然后执行javac TestJNI.java,如果没有报错,会生成一个TestJNI.class。
3.然后执行javah TestJNI,会生成一个文件TestJNI.h文件,其中有一个函数的声明如下:
JNIEXPORT void JNICALL Java_TestJNI_testjni(JNIEnv *, jobject, jstring);
4.创建文件testjni.c将上面那个函数实现,内容如下:
#include <stdio.h>
#include <TestJNI.h>
JNIEXPORT void JNICALL Java_weiqiong_testjni
(JNIEnv *env, jobject obj,jstring jstr)
{
    char* buffer = env->GetStringUTFChars(jstr,0);
    printf(buffer);
    env->ReleaseStringUTFChars(jstr,buffer);
}

5.为了生成.so文件,创建makefile文件如下:
libtestjni.so:testjni.o makefile
gcc -Wall -rdynamic -shared -o libtestjni.so testjni.o
testjni.o:testjni.c weiqiong.h
gcc -Wall -c testjni.c -I./ -I/usr/java/j2sdk1.5.0/include -I/usr/java/j2sdk1.5.0/include/linux
cl:
rm -rf *.o *.so
注意:gcc前面是tab空,j2sdk的目录根据自己装的j2sdk的具体版本来写,生成的so文件的名字必须是loadLibrary的参数名前加“lib”。

6.export LD_LIBRARY_PATH=.,由此设置library路径为当前目录,这样java文件才能找到so文件。一般的做法是将so文件copy到本机的LD_LIBRARY_PATH目录下。

7.执行java TestJNI,打印出结果:“haha---------go into c!!!”


三.JNI调用中遇到的问题
在首次使用JNI的时候有些疑问,后来在使用中一一解决,下面就是这些问题的备忘:

1. java和c是如何互通的?
其实不能互通的原因主要是数据类型的问题,jni解决了这个问题,例如那个c文件中的jstring数据类型就是java传入的String对象,经过jni函数的转化就能成为c的char*。
对应数据类型关系如下表:
Java 类型 本地c类型 说明
boolean jboolean 无符号,8 位
byte jbyte 无符号,8 位
char jchar 无符号,16 位
short jshort 有符号,16 位
int jint 有符号,32 位
long jlong 有符号,64 位
float jfloat 32 位
double jdouble 64 位
void void N/A

JNI 还包含了很多对应于不同 Java 对象的引用,就不一一说明了。


2. 如何将java传入的String参数转换为c的char*,然后使用?
java 传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);注意:

test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->ReleaseStringUTFChars(env, jstring, test);


四. JNI中对中文数据的处理


如果你的应用不涉及中文处理的问题,那么恭喜你,你的JNI应用应该是正常工作了。但是,作为炎黄子孙,我们的母语是汉语,我们的每个应用都不可避免的要遇到中文处理。可是无论是java还是c++都是基本不识汉字的外国大牛们研究出来的,所以虽然有相关的宽窄字节转换的接口可以调用,但是他们的表现实在是不能让人满意(至少是不能满足我的需求,^_^).

我们知道,JAVA采用UNICODE来支持运行时,当然类文件的保存格式是UTF-8.当我们需要使用JAVA和本地环境c++混合编程时候,问题就来了:JAVA如何传递UNICODE字符串到本地环境;本地环境如何传递UNICODE字符到JAVA环境。比如上面提到的const jbyte* GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy),就是JNI提供的UNICODE字符串到本地环境字符串的转换函数,不能处理含有中文的字符串的转换问题. 所以,要处理含有中文的字符串,必须要经过c++的再处理,才能正常运行,这里我们推荐wcstombs()和mbstowcs()标准cAPI,如果你是在windows下编程,不需考虑Linux环境的时候,可以考虑用WideCharToMultiByte()和MultiByteToWideChar()这两个win32API,具体应用请参考http://dev.csdn.net/article/82/82363.shtm.

wcstombs()和mbstowcs()函数示例如下:

#include <stdlib.h>
#include <stdio.h>

int main( void )
{
    setlocale(LC_ALL,"zh_CN.gb18030");
     //windows下为
    //setlocale(LC_ALL,"chs_chn");
    int i;
    char    *pmbnull  = NULL;
    char    *pmbhello = (char *)malloc( MB_CUR_MAX );
    wchar_t *pwchello = L"Hi";
    wchar_t *pwc      = (wchar_t *)malloc( sizeof( wchar_t ));

    printf( "Convert to multibyte string:/n" );
    i = wcstombs( pmbhello, pwchello, MB_CUR_MAX );
    printf( "  Characters converted: %u/n", i );
    printf( "  Hex value of first" );
    printf( " multibyte character: %#.4x/n/n", pmbhello[0] );

    printf( "Convert back to wide-character string:/n" );
    i = mbstowcs( pwc, pmbhello, MB_CUR_MAX );
    printf( "  Characters converted: %u/n", i );
    printf( "  Hex value of first" );
    printf( " wide character: %#.4x/n/n", pwc[0]);
}

另wchar_t的长度是由实现决定的,所以不同的编译器得到的结果也很有可能不同,具体讨论请参考:http://blog.vckbase.com/smileonce/archive/2004/12/09/1972.html
为了程序在linux下的正常工作,需做如下调整

typedef std::basic_string<jchar> XString;


// Converts from a XString to a std::wstring
std::wstring XstringToWstring(const XString& str)
{
    return std::wstring(str.begin( ), str.end( ));
}

// Converts from a std::wstring to a XString
XString wstringToXstring(const std::wstring& str)
{
    return XString(str.begin( ), str.end( ));
}
关于jchar与wchar_t的讨论可参考:宽字符标量L"xx"在VC6.0/7.0和GNU g++中的不同实现。

综上所述,若存在含有中文的字符串时,应将testjni.cpp改写如下:
#include <stdio.h>
#include <TestJNI.h>

typedef std::basic_string<jchar> XString;

JNIEXPORT void JNICALL Java_weiqiong_testjni
(JNIEnv *env, jobject obj,jstring jstr)
{
    jchar* jbuffer = env->GetStringChars(jstr,0);
    XString xstr(jbuffer);
    wstring wstr = wstring(xstr.begin(),xstr.end());

    wchar_t *pwchello = wstr.c_str();
    
    setlocale(setlocale(LC_ALL,"zh_CN.gb18030"));

    int size = wcstombs( NULL, pwchello, 0 );
    char    *pmbhello = (char *)malloc( 2*size+1 );
    printf( "Convert to multibyte string:/n" );
    int count = wcstombs( pmbhello, pwchello, 2*size+1 );
    printf( "  Characters converted: %u/n", count );
        printf( "  Hex value of first" );
        printf( " multibyte character: %#.4x/n/n", pmbhello );

    printf( "Convert back to wide-character string:/n" );
    wchar_t* pwc = (wchar_t*)malloc(2*size+1);
        count = mbstowcs( pwc, pmbhello, 2*size+1 );
        printf( "  Characters converted: %u/n", count );
       printf( "  Hex value of first" );
        printf( " wide character: %#.4x/n/n", pwc);

    free(pmbhello);
    free(pwc);    
    env->ReleaseStringChars(jstr,jbuffer);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值