今天要说的是如何利用JNI从本地代码来调用
Java代码。好,我们Step by Step:
Step 1:建立目录结构
要运行Java,首先要有JRE,所以我们要在程序目录下新建一个目录来放JRE。我这里使用的是JRE 6u1。我们只需要JRE目录下的bin和lib文件夹,
其他可以不要(其实这里面还有好多东西是用不着的,不过这里为了省事就不去删除了)。把这两个文件夹放在APP_HOME\jre下面(放在其他地方也可以,只要着两个文件夹在一起就行,后面改一下代码就好,下同)。
然后是放我们的java类文件的地方,我们叫他classes
最后是我们放第三方库的地方:lib
Step 2:编写&编译Java代码
这一步相信大家都会,我就不罗嗦了,只要最后吧对应的文件放到对应的地方就行。
本例中使用的代码为:
QUOTE:
package cn.bearice;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
public class JavaNativeCall{
private static final Logger logger = Logger.getLogger(JavaNativeCall.class);
static{
Logger.getRootLogger().addAppender(
new ConsoleAppender(new PatternLayout("%d{HH:mm:ss,SSS} [%t] %-5p %c %x - %m\n"),
ConsoleAppender.SYSTEM_ERR));
}
public static void execute(int arg1){
logger.info("Starting...");
System.out.format("arg1=[%d]%n",arg1);
logger.info("Exiting...");
}
}
为了简单,这里只是用了一个第三方库log4j(别跟我说你没听过-_-||)。好。把它编译,放在APP_HOME\classes\cn\bearice下面,把log4j.jar放在APP_HOME\lib下面。
Step 3:编写本地代码
示例代码如下:
QUOTE:
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#define CP_MAX 1024//注意:如果你用的第三方库比较多,记着把这个设置的大一点
int main(int c,char** v){
int ret = 0;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
jclass cls;
jmethodID mid;
JavaVMOption options[1];
char cp[CP_MAX];
sprintf(cp,"-Djava.class.path=%s",getenv("CLASSPATH"));
options[0].optionString = cp;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE;
vm_args.version = JNI_VERSION_1_6;
puts("Create JVM\n");
ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if(jvm==NULL)goto JVMCreationFailed;
puts("Find class\n");
cls = env->FindClass("cn/bearice/JavaNativeCall");
if(cls==NULL)goto exception;
puts("Get Method\n");
mid = env->GetStaticMethodID(cls, "execute", "(I)V");
if(cls==NULL)goto exception;
puts("Invoke method\n");
env->CallStaticVoidMethod(cls, mid, 15);
exception:
puts("Exception\n");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
}
goto destory;
JVMCreationFailed:
printf("JVM Creation failed. ret=[%x]",ret);
goto destory;
destory:
puts("Destory JVM\n");
if(jvm!=NULL)jvm->DestroyJavaVM();
return ret;
}
BTW:这里使用了一些goto,但是在实际编程中,能不用goto尽量不要用,可以避免很多问题的。
Setp 4:编译代码
我使用gcc编译的,和平时编译的时候差不多,但是有几点需要注意:
QUOTE:
1:编译的时候需要包含jni.h(在JDK_HOME\include下),所以别忘了加参数。而且还要include win32子目录
2:链接时需要jvm.lib(在JDK_HOME\lib下),还是别忘了加参数
3:一些问题:
有时候连接的时候会提示找不到引用之类的(我就在这里费了好大的劲),这时还要特别操作一下:
1)进入JDK_HOME\lib,吧jvm.dll考过来(在JRE_HOME\bin\client下)
2)新建文件:jvm.def,内容如下:
EXPORTS
JNI_CreateJavaVM@12
JNI_GetDefaultJavaVMInitArgs@4
JNI_GetCreatedJavaVMs@12
3)打开控制台,运行:
dlltool --input-def jvm.def --kill-at --dllname jvm.dll --output-lib libjvm.dll.a
然后就应该正常链接了。
这是我的编译命令(其实是devcpp生成的):
g++.exe -c main.c -o main.o -I"g:/Dev-Cpp/lib/gcc/mingw32/3.4.2/include" -I"g:/Dev-Cpp/include/c++/3.4.2/backward" -I"g:/Dev-Cpp/include/c++/3.4.2/mingw32" -I"g:/Dev-Cpp/include/c++/3.4.2" -I"g:/Dev-Cpp/include" -I"G:/Program Files/Java/jdk1.6.0_01/include" -I"G:/Program Files/Java/jdk1.6.0_01/include/win32"
g++.exe main.o -o "JavaNativeCall.exe" -L"g:/Dev-Cpp/lib" -L"G:/Program Files/Java/jdk1.6.0_01/lib" -Wl,-ljvm
Step 5:最后的准备工作
下面我们来配置类路径(别跟我说不知道什么叫类路径-_-b)
因为MS这样调用的jvm不会读取环境变量,我就写了一段根据环境变量配置类路径的(看上面的代码)。下面我们的工作就是配置环境变量。我写了一个批处理来解决这个问题:
QUOTE:
@echo off
SETLOCAL
set PATH=%PATH%;./jre/bin;./jre/bin/client
FOR /F "usebackq" %%i IN (`cd`) DO SET CURDIR=%%i
for %%a in (%CURDIR%\lib\*.jar) do set CLASSPATH=%%a;%CLASSPATH%
set CLASSPATH=%CLASSPATH%;%CURDIR%./classes
JavaNativeCall.exe
pause
好了,万事俱备,我们运行一下看看:
M:\Dev-Cpp\JavaNativaCall>run
Create JVM
Find class
Get Method
Invoke method
03:42:39,699 [main] INFO cn.bearice.JavaNativeCall - Starting...
arg1=[15]
03:42:39,879 [main] INFO cn.bearice.JavaNativeCall - Exiting...
Exception
Destory JVM
请按任意键继续. . .
OK 正常运行,我们的任务也算完成了,至于完成的好不好,就有各位看官点评吧。时候不早,睡觉刚好~
Hibernate去鸟~~
PS:相关文件我就不上传了,大家有兴趣的自己实践去。下回些什么呢?准备用Java写键盘钩子~
[
本帖最后由 Icybear 于 2007-7-19 04:07 编辑 ]