7.4.3 在C/C++程序中启动Java虚拟机
上面介绍的这两个使用JNI的场景都是以Java程序为主体的。用户使用的是Java程序,只是程序的部分组件由原生代码来实现。下面介绍的第三个场景以C/C++程序为主体,把Java虚拟机嵌入到原生代码中。通过这种方式,可以在C/C++程序中调用由Java编写的组件。实际上,Java虚拟机本身是通过原生代码来实现的,要在C/C++程序中调用并非难事。在下面的示例中,C++程序需要获取一个网页的内容之后再进行处理。由于Java程序中包含的网络相关的类库比较丰富,获取网页的功能由Java程序来实现,而对网页内容的处理则由C++代码来完成。对此,创建了一个Java类com.java7book.chapter7.jni.WebPageDownloader,其中的静态方法getContent用来根据网页的URL返回其中的内容。在C++程序中启动Java虚拟机如代码清单7-22所示,方法JNI_CreateJavaVM用来创建并启动一个Java虚拟机。在创建时需要提供虚拟机的启动参数。这里只提供了Java类文件所在的路径,用于查找所需的Java类。Java虚拟机创建成功之后,JavaVM表示虚拟机对象,可以利用JNIEnv中的方法来查找Java类并调用其中的方法。在使用完虚拟机之后,需要通过DestroyJavaVM方法来销毁虚拟机。在链接C++代码时需要把JDK的lib目录添加到搜索路径中。
代码清单7-22 在C++程序中启动Java虚拟机
include<jni.h>
int main()
{
JNIEnv*env;
JavaVM*jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString="-Djava.class.path=C:\java7\code\chapter7\bin";
vm_args.version=JNI_VERSION_1_6;
vm_args.options=options;
vm_args.nOptions=1;
vm_args.ignoreUnrecognized=0;
jint res;
res=JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
if(res<0)
{
return res;
}
jclass clsDownloader=env->FindClass("com/java7book/chapter7/jni/WebPageDownloader");
jmethodID midGetContent=env->GetStaticMethodID(clsDownloader,"getContent","(Ljava/lang/String;)Ljava/lang/String;");
jstring content=(jstring)env->CallStaticObjectMethod(clsDownloader, midGetContent, env->NewStringUTF("http://www.baidu.com"));
if(env->ExceptionOccurred())
{
printf("Error occurs when downloading content.");
jvm->DestroyJavaVM();
return-1;
}
const char*text_str=env->GetStringUTFChars(content, NULL);
printf(text_str);
jvm->DestroyJavaVM();
}
上面的代码也说明了C++代码对Java代码中产生的异常的处理方式。在C++代码中,可以通过JNIEnv中的ExceptionOccurred方法来检查Java代码中是否产生了异常,如果产生了异常,那么会执行相应的异常处理逻辑。