Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2876689
  • 博文数量: 674
  • 博客积分: 17881
  • 博客等级: 上将
  • 技术积分: 4849
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 10:15
文章分类

全部博文(674)

文章存档

2013年(34)

2012年(146)

2011年(197)

2010年(297)

分类: LINUX

2011-05-27 18:00:27

一、Windows版本
1、创建工作目录JNIDemo。
2、编写包含本地方法的Java类JNIDemo.java。
    首先为Java的package在工作目录下创建文件夹层次结构test\jni\demo,在该子目录下创建Java源文件。
view plaincopy to clipboardprint?
package test.jni.demo;  
 
public class JNIDemo{  
 
    //Dynamic library initialization  
    static {  
        try{  
            System.loadLibrary("jnidemo");  
        }catch(SecurityException e){  
            e.printStackTrace();  
            throw new RuntimeException("A security manager exists and its checkLink method doesn't allow loading of the specified dynamic library jnidemo.dll");  
        }catch(UnsatisfiedLinkError e){  
            e.printStackTrace();  
            throw new RuntimeException("jnidemo.dll does not exist");  
        }  
    }  
 
    //Native method,input a String object,show the message and return a String object  
    public static native String showMessage(String msg);  
 
    //Main method,just for test  
    public static void main(String []args){  
        String msg = JNIDemo.showMessage("Hello JNI");  
        System.out.println(msg);  
    }  

package test.jni.demo;
public class JNIDemo{
 //Dynamic library initialization
 static {
  try{
   System.loadLibrary("jnidemo");
  }catch(SecurityException e){
   e.printStackTrace();
   throw new RuntimeException("A security manager exists and its checkLink method doesn't allow loading of the specified dynamic library jnidemo.dll");
  }catch(UnsatisfiedLinkError e){
   e.printStackTrace();
   throw new RuntimeException("jnidemo.dll does not exist");
  }
 }
 //Native method,input a String object,show the message and return a String object
 public static native String showMessage(String msg);
 //Main method,just for test
 public static void main(String []args){
  String msg = JNIDemo.showMessage("Hello JNI");
  System.out.println(msg);
 }
}
3、设置环境变量,为编译Java源文件和产生C代码头文件做准备,前提是要实现设置好JAVA_HOME和CLASSPATH环境变量,否则相应指定需要的绝对路径。
view plaincopy to clipboardprint?
set PATH=%JAVA_HOME%\bin;%PATH%  
set CLASSPATH=.;%CLASSPATH% 
set PATH=%JAVA_HOME%\bin;%PATH%
set CLASSPATH=.;%CLASSPATH%
 4、编译Java源文件。
view plaincopy to clipboardprint?
javac test\jni\demo\JNIDemo.java 
javac test\jni\demo\JNIDemo.java
5、产生C头文件。
view plaincopy to clipboardprint?
javah -jni -o jnidemo.h test.jni.demo.JNIDemo 
javah -jni -o jnidemo.h test.jni.demo.JNIDemo
6、此时工作目录下将生成jnidemo.h文件,新建jnidemo.c文件实现该本地接口。
view plaincopy to clipboardprint?
#include   
#include "jnidemo.h"  
 
JNIEXPORT jstring JNICALL Java_test_jni_demo_JNIDemo_showMessage(JNIEnv *env,   
        jclass classObject,  
        jstring valueObject){  
    jclass clsString;  
    jstring strEncode;  
    jmethodID mid;  
    jbyteArray barr;  
    jsize alen;  
    jbyte *ba;  
    char *rtn = NULL;  
    char msg[1024];  
    clsString = (*env)->FindClass(env,"java/lang/String");  
    strEncode = (*env)->NewStringUTF(env,"UTF-8");//Support for multibyte character language  
    mid = (*env)->GetMethodID(env,clsString, "getBytes", "(Ljava/lang/String;)[B");  
    barr = (jbyteArray)(*env)->CallObjectMethod(env,valueObject,mid,strEncode);  
    alen = (*env)->GetArrayLength(env,barr);  
    ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);  
    if(alen > 0){  
        rtn = (char*)malloc(alen+1);  
        memcpy(rtn,ba,alen);  
        rtn[alen]=0;  
    }  
    (*env)->ReleaseByteArrayElements(env,barr,ba,0);  
    MessageBox(NULL,(LPCTSTR)rtn,"JNIDemo",MB_OK);//Call Win32 API    
    memset(msg,'\0',sizeof(msg));  
    sprintf(msg,"JNI method receive the message : %s\n",rtn);//Risky code  
    if(rtn){  
        free(rtn);  
    }  
    mid = (*env)->GetMethodID(env,clsString,"","([BLjava/lang/String;)V");  
    strEncode = (*env)->NewStringUTF(env,"GBK");//Support for chinese  
    alen = strlen(msg);  
    barr = (*env)->NewByteArray(env,alen);  
    (*env)->SetByteArrayRegion(env,barr,0,alen,(jbyte*)msg);  
    return (jstring)(*env)->NewObject(env,clsString,mid,barr,strEncode);  

#include
#include "jnidemo.h"
JNIEXPORT jstring JNICALL Java_test_jni_demo_JNIDemo_showMessage(JNIEnv *env,
  jclass classObject,
  jstring valueObject){
 jclass clsString;
 jstring strEncode;
 jmethodID mid;
 jbyteArray barr;
 jsize alen;
 jbyte *ba;
 char *rtn = NULL;
 char msg[1024];
 clsString = (*env)->FindClass(env,"java/lang/String");
 strEncode = (*env)->NewStringUTF(env,"UTF-8");//Support for multibyte character language
 mid = (*env)->GetMethodID(env,clsString, "getBytes", "(Ljava/lang/String;)[B");
 barr = (jbyteArray)(*env)->CallObjectMethod(env,valueObject,mid,strEncode);
 alen = (*env)->GetArrayLength(env,barr);
 ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
 if(alen > 0){
  rtn = (char*)malloc(alen+1);
  memcpy(rtn,ba,alen);
  rtn[alen]=0;
 }
 (*env)->ReleaseByteArrayElements(env,barr,ba,0);
 MessageBox(NULL,(LPCTSTR)rtn,"JNIDemo",MB_OK);//Call Win32 API 
 memset(msg,'\0',sizeof(msg));
 sprintf(msg,"JNI method receive the message : %s\n",rtn);//Risky code
 if(rtn){
  free(rtn);
 }
 mid = (*env)->GetMethodID(env,clsString,"","([BLjava/lang/String;)V");
 strEncode = (*env)->NewStringUTF(env,"GBK");//Support for chinese
 alen = strlen(msg);
 barr = (*env)->NewByteArray(env,alen);
 (*env)->SetByteArrayRegion(env,barr,0,alen,(jbyte*)msg);
 return (jstring)(*env)->NewObject(env,clsString,mid,barr,strEncode);
}

7、编译动态库,由于不想启动msdv,所以使用namke和cl.exe、link.exe解决它。
view plaincopy to clipboardprint?
F:\workshop\Test\JNIDemo>"D:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT" 
F:\workshop\Test\JNIDemo>"D:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"
设置环境变量后编写makefile,内容如下。
view plaincopy to clipboardprint?
CC=cl.exe  
LD=link.exe  
 
CFLAGS=/nologo /MT /W3 /Gi /GX /O2 /I "D:\Program Files\Java\jdk1.6.0_07\include\win32" /I "D:\Program Files\Java\jdk1.6.0_07\include" /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNIDEMO_EXPORTS" /Fd"jnidemo.pdb" /FD /c   
LDFLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /pdb:"jnidemo.pdb" /machine:I386 /out:"jnidemo.dll" /implib:"jnidemo.lib" /libpath:"D:\Program Files\Java\jdk1.6.0_07\lib" 
 
SOURCES=jnidemo.c  
 
OBJECTS=*.obj  
 
all:  
    $(CC) $(CFLAGS)  $(SOURCES)  
    $(LD) $(LDFLAGS) $(OBJECTS)  
 
javah:  
    javac test/jni/demo/JNIDemo.java  
    javah -jni -o jnidemo.h test.jni.demo.JNIDemo  
 
run:  
    java test.jni.demo.JNIDemo  
 
clean:  
    del /F jnidemo.h  
    del /F test\jni\demo\*.class 
    del /F *.dll  
    del /F *.obj  
    del /F *.idb  
    del /F *.exp  
    del /F *.lib 
CC=cl.exe
LD=link.exe
CFLAGS=/nologo /MT /W3 /Gi /GX /O2 /I "D:\Program Files\Java\jdk1.6.0_07\include\win32" /I "D:\Program Files\Java\jdk1.6.0_07\include" /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNIDEMO_EXPORTS" /Fd"jnidemo.pdb" /FD /c
LDFLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /pdb:"jnidemo.pdb" /machine:I386 /out:"jnidemo.dll" /implib:"jnidemo.lib" /libpath:"D:\Program Files\Java\jdk1.6.0_07\lib"
SOURCES=jnidemo.c
OBJECTS=*.obj
all:
 $(CC) $(CFLAGS)  $(SOURCES)
 $(LD) $(LDFLAGS) $(OBJECTS)
javah:
 javac test/jni/demo/JNIDemo.java
 javah -jni -o jnidemo.h test.jni.demo.JNIDemo
run:
 java test.jni.demo.JNIDemo
clean:
 del /F jnidemo.h
 del /F test\jni\demo\*.class
 del /F *.dll
 del /F *.obj
 del /F *.idb
 del /F *.exp
 del /F *.lib

之后顺序执行nmake javah、nmake all、nmake run即可见到jni接口执行效果,首先消息框显示java输入的参数,点击确定后控制台打印jni方法返回的字符串。
 
二、Linux版本
由于实现方式类似,仅编译工具和使用的API不同,过程不再赘述,仅给是文件内容、命令和运行效果。
1、创建工作目录。
view plaincopy to clipboardprint?
[tbicl@vipcms test]$ mkdir JNIDemo  
[tbicl@vipcms test]$ cd JNIDemo/  
[tbicl@vipcms JNIDemo]$ mkdir test  
[tbicl@vipcms JNIDemo]$ cd test/  
[tbicl@vipcms test]$ mkdir jni  
[tbicl@vipcms test]$ cd jni/  
[tbicl@vipcms jni]$ mkdir demo  
[tbicl@vipcms jni]$ cd demo/  
[tbicl@vipcms demo]$ vim JNIDemo.java 
[tbicl@vipcms test]$ mkdir JNIDemo
[tbicl@vipcms test]$ cd JNIDemo/
[tbicl@vipcms JNIDemo]$ mkdir test
[tbicl@vipcms JNIDemo]$ cd test/
[tbicl@vipcms test]$ mkdir jni
[tbicl@vipcms test]$ cd jni/
[tbicl@vipcms jni]$ mkdir demo
[tbicl@vipcms jni]$ cd demo/
[tbicl@vipcms demo]$ vim JNIDemo.java
2、Java源文件和windows版的一致,仅修改提示信息。
view plaincopy to clipboardprint?
package test.jni.demo;  
 
public class JNIDemo{  
 
    //Dynamic library initialization  
    static {  
        try{  
            System.loadLibrary("jnidemo");  
        }catch(SecurityException e){  
            e.printStackTrace();  
            throw new RuntimeException("A security manager exists and its checkLink method doesn't allow loading of the specified dynamic lib  
rary libjnidemo.so");  
        }catch(UnsatisfiedLinkError e){  
            e.printStackTrace();  
            throw new RuntimeException("libjnidemo.so does not exist");  
        }  
    }  
 
    //Native method,input a String object,show the message and return a String object  
    public static native String showMessage(String msg);  
 
    //Main method,just for test  
    public static void main(String []args){  
        String msg = JNIDemo.showMessage("Hello JNI");  
        System.out.println(msg);  
    }  

package test.jni.demo;
public class JNIDemo{
    //Dynamic library initialization
    static {
        try{
            System.loadLibrary("jnidemo");
        }catch(SecurityException e){
            e.printStackTrace();
            throw new RuntimeException("A security manager exists and its checkLink method doesn't allow loading of the specified dynamic lib
rary libjnidemo.so");
        }catch(UnsatisfiedLinkError e){
            e.printStackTrace();
            throw new RuntimeException("libjnidemo.so does not exist");
        }
    }
    //Native method,input a String object,show the message and return a String object
    public static native String showMessage(String msg);
    //Main method,just for test
    public static void main(String []args){
        String msg = JNIDemo.showMessage("Hello JNI");
        System.out.println(msg);
    }
}
3、直接编写makefile搞定剩下的事。
view plaincopy to clipboardprint?
CC=cc -g  
 
LIBS= -L${JAVA_HOME}/lib  
 
CFLAGS= -Wl,-soname,libjnidemo.so -fPIC -shared  
SOURCES=jnidemo.c  
 
all:  
    $(CC) $(CFLAGS) $(LIBS) -o libjnidemo.so $(SOURCES)  
 
javah:  
    javac -cp . test/jni/demo/JNIDemo.java  
    javah -classpath . -jni -o jnidemo.h test.jni.demo.JNIDemo  
 
run:  
    export LD_LIBRARY_PATH=.:${LD_LIBRARY_PATH}  
    java test.jni.demo.JNIDemo  
 
clean:  
    rm -rf ../*.so  
    rm -rf ../jnidemo.h  
    rm -rf test/jni/demo/*.class 
CC=cc -g
LIBS= -L${JAVA_HOME}/lib
CFLAGS= -Wl,-soname,libjnidemo.so -fPIC -shared
SOURCES=jnidemo.c
all:
    $(CC) $(CFLAGS) $(LIBS) -o libjnidemo.so $(SOURCES)
javah:
    javac -cp . test/jni/demo/JNIDemo.java
    javah -classpath . -jni -o jnidemo.h test.jni.demo.JNIDemo
run:
    export LD_LIBRARY_PATH=.:${LD_LIBRARY_PATH}
    java test.jni.demo.JNIDemo
clean:
    rm -rf ../*.so
    rm -rf ../jnidemo.h
    rm -rf test/jni/demo/*.class
4、产生头文件。
view plaincopy to clipboardprint?
[tbicl@vipcms JNIDemo]$ make javah  
javac -cp . test/jni/demo/JNIDemo.java  
javah -classpath . -jni -o jnidemo.h test.jni.demo.JNIDemo  
[tbicl@vipcms JNIDemo]$ ll  
total 12  
-rw-rw-r--  1 tbicl tbicl  490 Aug 20 23:38 jnidemo.h  
-rw-rw-r--  1 tbicl tbicl  424 Aug 20 23:38 makefile  
drwxrwxr-x  3 tbicl tbicl 4096 Aug 20 23:23 test 
[tbicl@vipcms JNIDemo]$ make javah
javac -cp . test/jni/demo/JNIDemo.java
javah -classpath . -jni -o jnidemo.h test.jni.demo.JNIDemo
[tbicl@vipcms JNIDemo]$ ll
total 12
-rw-rw-r--  1 tbicl tbicl  490 Aug 20 23:38 jnidemo.h
-rw-rw-r--  1 tbicl tbicl  424 Aug 20 23:38 makefile
drwxrwxr-x  3 tbicl tbicl 4096 Aug 20 23:23 test
5、修改jnidemo.c。
view plaincopy to clipboardprint?
#include "jnidemo.h"  
 
JNIEXPORT jstring JNICALL Java_test_jni_demo_JNIDemo_showMessage(JNIEnv *env,  
        jclass classObject,  
        jstring valueObject){  
    jclass clsString;  
    jstring strEncode;  
    jmethodID mid;  
    jbyteArray barr;  
    jsize alen;  
    jbyte *ba;  
    char *rtn = NULL;  
    char msg[1024];  
    clsString = (*env)->FindClass(env,"java/lang/String");  
    strEncode = (*env)->NewStringUTF(env,"UTF-8");//Support for multibyte character language  
    mid = (*env)->GetMethodID(env,clsString, "getBytes", "(Ljava/lang/String;)[B");  
    barr = (jbyteArray)(*env)->CallObjectMethod(env,valueObject,mid,strEncode);  
    alen = (*env)->GetArrayLength(env,barr);  
    ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);  
    if(alen > 0){  
        rtn = (char*)malloc(alen+1);  
        memcpy(rtn,ba,alen);  
        rtn[alen]=0;  
    }  
    (*env)->ReleaseByteArrayElements(env,barr,ba,0);  
    //MessageBox(NULL,(LPCTSTR)rtn,"JNIDemo",MB_OK);//Call Win32 API      
    fprintf(stdout,"%s\n",rtn);  
    memset(msg,'\0',sizeof(msg));  
    sprintf(msg,"JNI method receive the message : %s\n",rtn);//Risky code  
    if(rtn){  
        free(rtn);  
    }  
    mid = (*env)->GetMethodID(env,clsString,"","([BLjava/lang/String;)V");  
    strEncode = (*env)->NewStringUTF(env,"GBK");//Support for chinese  
    alen = strlen(msg);  
    barr = (*env)->NewByteArray(env,alen);  
    (*env)->SetByteArrayRegion(env,barr,0,alen,(jbyte*)msg);  
    return (jstring)(*env)->NewObject(env,clsString,mid,barr,strEncode);  

#include "jnidemo.h"
JNIEXPORT jstring JNICALL Java_test_jni_demo_JNIDemo_showMessage(JNIEnv *env,
        jclass classObject,
        jstring valueObject){
    jclass clsString;
    jstring strEncode;
    jmethodID mid;
    jbyteArray barr;
    jsize alen;
    jbyte *ba;
    char *rtn = NULL;
    char msg[1024];
    clsString = (*env)->FindClass(env,"java/lang/String");
    strEncode = (*env)->NewStringUTF(env,"UTF-8");//Support for multibyte character language
    mid = (*env)->GetMethodID(env,clsString, "getBytes", "(Ljava/lang/String;)[B");
    barr = (jbyteArray)(*env)->CallObjectMethod(env,valueObject,mid,strEncode);
    alen = (*env)->GetArrayLength(env,barr);
    ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
    if(alen > 0){
        rtn = (char*)malloc(alen+1);
        memcpy(rtn,ba,alen);
        rtn[alen]=0;
    }
    (*env)->ReleaseByteArrayElements(env,barr,ba,0);
    //MessageBox(NULL,(LPCTSTR)rtn,"JNIDemo",MB_OK);//Call Win32 API   
    fprintf(stdout,"%s\n",rtn);
    memset(msg,'\0',sizeof(msg));
    sprintf(msg,"JNI method receive the message : %s\n",rtn);//Risky code
    if(rtn){
        free(rtn);
    }
    mid = (*env)->GetMethodID(env,clsString,"","([BLjava/lang/String;)V");
    strEncode = (*env)->NewStringUTF(env,"GBK");//Support for chinese
    alen = strlen(msg);
    barr = (*env)->NewByteArray(env,alen);
    (*env)->SetByteArrayRegion(env,barr,0,alen,(jbyte*)msg);
    return (jstring)(*env)->NewObject(env,clsString,mid,barr,strEncode);
}
 
6、编译并运行。
 
三、总结
别人所jni是双刃剑,使好了很有用处,否则带来一堆烦恼,正如c代码中标注的危险代码部分,如果Java方法输入的参数足够大,导致msg数组无法容纳,sprintf将引发问题,由于通常都是通过jvm启动并载入动态库本地方法,本地方法和jvm使用同一地址空间,本地方法crash将导致jvm奔溃直接退出,如果这种情况放生诸如WebLogic等追求很大稳定性的中间件之上,后果可想而知,所以这洪水能回避就尽量回避,除非本地接口简单得出错的几率都没有,又或者编写者非常神仙能把握好它。
 
阅读(1224) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~