一、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等追求很大稳定性的中间件之上,后果可想而知,所以这洪水能回避就尽量回避,除非本地接口简单得出错的几率都没有,又或者编写者非常神仙能把握好它。