在看到《The Java™ Native Interface》回调接口这一章时,突然觉得Google创造出Android系统实在是一件很自然的事情。并且觉得其中的技术演进、跨度其实很缓慢并且是很小的。Java虚拟机的概念从其一诞生就一直延续到今天,虚拟机也是Java最核心最本质的东西。Java Applet在目前看来虽然是一个很老旧、很过时的东西,但是它所展现出来的价值确实十分巨大的。这意味着我们可以在大部分的平台上创建Java虚拟机,然后通过Java虚拟机来执行Java二进制字节码。按照这个思路,我们在Linux内核启动之后,就创建出一个虚拟机,然后在该虚拟机上执行我们的Java应用程序代码,这就是Android的雏形。这样看来,觉得国内与国外其实最大的差距并不是所谓的创新能力不足,而真正的原因其实应该是基础科学缺乏研究与积累。
虽然书中原理讲的比较清楚,但是要运行起这样一个实例来还是比较困难。下面我们就讲一下相应的步骤(一些设置需要参考前面文章):
1.Eclipse中创建JNI_HelloWorld工程
文件组织形式及结构如下:
实现代码如下(HelloWorld.java):
package com.worthcloud;
public class HelloWorld {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello World");
}
}
2. vs2013创建一个空解决方案,然后在方案中添加一个win32控制台工程
建立好后,注意调整为x64版本,配置好jni.h等头文件的引用目录,lib库的引用目录(注:这里我们将C:\Program Files\Java\jdk1.8.0_31\lib目录下的jvm.lib拷贝到我们工程中,用户也可以不用拷贝,只需要链接时能找到该文件即可)
3. 编写C++程序代码,创建虚拟机执行上面的HelloWorld.main程序代码
// JNI_HelloWorld.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <jni.h>
#define PATH_SEPARATOR ";" /*define it to be ':' on solaris*/
#define USER_CLASSPATH "..\\..\\bin" /*where HelloWorld.class is*/
#define HELLOWORLD_CLASS_NAME "com/worthcloud/HelloWorld"
int _tmain(int argc, _TCHAR* argv[])
{
JNIEnv *env = NULL;
JavaVM *jvm = NULL;
jint res = 0;
jclass cls;
jmethodID methodID;
jstring jstr;
jclass stringCls;
jobjectArray args;
#if defined(JNI_VERSION_1_4) || defined(JNI_VERSION_1_6) || defined(JNI_VERSION_1_8)
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString = "-Djava.class.path="USER_CLASSPATH;
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE;
/*Create the Java VM*/
res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
#elif defined (JNI_VERSION_1_2)
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString = "-Djava.class.path="USER_CLASSPATH;
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE;
/*Create the Java VM*/
res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
#else
/*current may not support*/
JDK1_1InitArgs vm_args;
char classpath[1024];
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif
if (res < 0)
{
fprintf(stderr, "Can't Create Java VM\n");
return -1;
}
cls = env->FindClass(HELLOWORLD_CLASS_NAME);
if (cls == NULL)
{
res = -2;
goto destroy;
}
methodID = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
if (methodID == NULL)
{
res = -3;
goto destroy;
}
jstr = env->NewStringUTF("from C++");
if (jstr == NULL)
{
res = -4;
goto destroy;
}
stringCls = env->FindClass("java/lang/String");
if (stringCls == NULL)
{
res = -5;
goto destroy;
}
args = env->NewObjectArray(1, stringCls, jstr);
if (args == NULL)
{
res = -6;
goto destroy;
}
env->CallStaticVoidMethod(cls, methodID, args);
destroy:
if (env->ExceptionOccurred())
{
env->ExceptionDescribe();
}
jvm->DestroyJavaVM();
if (res < 0)
{
printf("failure:%d\n", res);
}
return res;
}
4:编译链接
这里编译、链接不会出什么问题,但是在执行时就是会报告找不到依赖库jvm.dll的情况,你将C:\Program Files\Java\jdk1.8.0_31\jre\bin\server目录下的jvm.dll拷贝到你工程下的任何一个目录,都提示找不到。这需要用如下的方法进行设置:
在PATH环境变量中添加如下两项:
C:\Program Files\Java\jdk1.8.0_31\jre\bin
C:\Program Files\Java\jdk1.8.0_31\jre\bin\server
设置好后cmd查看一下,并重启vs2013:
5:执行