在Android的世界中,由名称为app_process的C++本地应用程序(路径为:framework/base/cmds/app_process/app_main.cpp)调用JNI Invocation API 在自身进程中加载dalvikvm虚拟机,这样就开创了java世界.
现在就简单的Demo一下这个原理,在Ubuntu11.10的终端中操作,已安装了jdk的条件。
1.首先创建一个工作目录:mkdir javaVMTest
2.创建一个java文件,Called.java,内容:
- public class Called
- {
- public static void main(String[] args)
- {
- // 把参数打印出来
- System.out.println(args[0]);
- }
- }
public class Called
{
public static void main(String[] args)
{
// 把参数打印出来
System.out.println(args[0]);
}
}
3.使用下面的命令将这个java文件编译为class文件,生成的class文件就在当前目录下: javac Called;
4.编写本地的C/C++程序,此处以C为例,名字为:invocationApi.c
- #include <jni.h> /* where everything is defined */
- int main()
- {
- JavaVM *vm; /* denotes a Java VM */
- JNIEnv *env; /* pointer to native method interface */
- JavaVMInitArgs vm_args; /* JDK 6 VM initialization arguments */
- JavaVMOption* options = new JavaVMOption[1];
- //options[0].optionString = "-Djava.class.path=/usr/lib/java";
- options[0].optionString = "-Djava.class.path=/home/joy/android4.0.3/external/javaVMTest";
- vm_args.version = JNI_VERSION_1_6;
- vm_args.nOptions = 1;
- vm_args.options = options;
- vm_args.ignoreUnrecognized = false;
- /* load and initialize a Java VM, return a JNI interface
- * pointer in env */
- JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
- //delete options;
- jclass cls = (*env).FindClass("Called");
- //printf("%p %d %d\n",cls,size,a);
- printf("%p \n",cls);
- jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
- jstring jstr = env->NewStringUTF("Hello JNI Invocation API !!!");
- jclass stringClass = env->FindClass("java/lang/String");
- jobjectArray args = env->NewObjectArray(1,stringClass,jstr);
- env->CallStaticVoidMethod(cls, mid, args);
- /* We are done. */
- vm->DestroyJavaVM();
- return 0;
- }
#include <jni.h> /* where everything is defined */ int main() { JavaVM *vm; /* denotes a Java VM */ JNIEnv *env; /* pointer to native method interface */ JavaVMInitArgs vm_args; /* JDK 6 VM initialization arguments */ JavaVMOption* options = new JavaVMOption[1]; //options[0].optionString = "-Djava.class.path=/usr/lib/java"; options[0].optionString = "-Djava.class.path=/home/joy/android4.0.3/external/javaVMTest"; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = false; /* load and initialize a Java VM, return a JNI interface * pointer in env */ JNI_CreateJavaVM(&vm, (void**)&env, &vm_args); //delete options; jclass cls = (*env).FindClass("Called"); //printf("%p %d %d\n",cls,size,a); printf("%p \n",cls); jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V"); jstring jstr = env->NewStringUTF("Hello JNI Invocation API !!!"); jclass stringClass = env->FindClass("java/lang/String"); jobjectArray args = env->NewObjectArray(1,stringClass,jstr); env->CallStaticVoidMethod(cls, mid, args); /* We are done. */ vm->DestroyJavaVM(); return 0; }
5.因为在这个c文件中用到了JDK中虚拟机相关的共享库,所以需要先找到JDK中的头文件位置,下面的命令可以对JDK进行快速定位:
which javac;
这样jdk的位置就能找到了,一般都在/usr/lib/jvm下
这里就用/usr/lib/jvm/java-6-sun-1.6.0.16/来代替。
6.在上一步骤中确定了编译时需要用到的头文件的位置,但还要确定链接运行需要的共享库位置:libjvm.so
这个文件一般都在jdk路径下面的:/jre/lib/amd64/server或者是什么i386等等的,用find -name "libjvm.so"能很快找到。
7.配置编译时连接库,打开一个终端执行下列命令,输出一个链接环境变量:
export LD_LIBRARY_PATH=/usr/lib/jvm/java-6-sun-1.6.0.16
/jre/lib/amd64/server
8.在上述配置好了环境的终端中,执行下列命令进行编译:
g++ -I /usr/lib/jvm/java-6-sun-1.6.0.16
/include
-I /usr/lib/jvm/java-6-sun-1.6.0.16
/include/linux -ljvm
-L/usr/lib/jvm/java-6-sun-1.6.0.16
/jre/lib/amd64/server invocationApi.c
9.执行上述编译产生的目标文件,可以看到运行结果:./a.out
如果文件正确执行,可以在终端中看到如下字符串:
Hello JNI Invocation API !!!
总结,在上述步骤中,我们制作了一个class文件和一个C文件,C文件里调用JVM相关的JVM创建函数,创建了一个JVM虚拟机,然后并构造了恰当的参数,再调用JVM相关的class文件执行函数,执行编译好的class文件,最终得到我们想要的结果。