Android系统基于Linux,它在用户空间的第一个进程是init进程,是属于native性质的进程,但是每个android应用都是跑在虚拟机下边的,第一个虚拟机进程zygote就是被init进程给fork出来的,这就涉及到了如何用native的c/c++代码启动jvm,本文就是做一个小示例来演示一下。
新建一个目录,添加main.cpp,代码如下:
#include<iostream> #include<jni.h> #include<cstdlib> using namespace std; int main(){ JavaVM* mJavaVM; JNIEnv* env; const int size = 3; JavaVMOption options[size]; options[0].optionString = "-Djava.compiler=NONE"; options[1].optionString = "-Djava.class.path=./"; //指定当前目录为classpath路径 options[2].optionString= "-verbose:class"; JavaVMInitArgs initArgs; initArgs.version = JNI_VERSION_1_4; initArgs.options = options; initArgs.nOptions = size; initArgs.ignoreUnrecognized = JNI_TRUE; if (JNI_CreateJavaVM(&mJavaVM, (void **)&env, &initArgs) != 0) { cout<<"创建JVM失败!"<<endl; }else{ cout<<"创建JVM成功!"<<endl; jclass startClass =env->FindClass("com/hello/HelloWorld"); jmethodID startMeth; if(startClass){ cout<<"成功找到类!"<<endl; startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V"); env->CallStaticVoidMethod(startClass, startMeth, NULL); }else{ cout<<"找不到指定的类!"<<endl; } } return 0; }
第一个和第二个参数都是输入参数,放到函数中被初始化的。第三个参数应传递JavaVMInitArgs结构体的指针,JavaVMInitArgs的定义如下:
typedef struct JavaVMInitArgs { jint version; jint nOptions; JavaVMOption *options; jboolean ignoreUnrecognized; } JavaVMInitArgs;
2.nOptions代表下边的JavaVMOption数组的个数
3.options代表JavaVMOption 数组类型,JavaVMOption的定义为:
typedef struct JavaVMOption { char *optionString; /* the option as a string in the default platform encoding */ void *extraInfo; } JavaVMOption;4.ignoreUnrecognized值为JNI_FALSE或者JNI_TRUE,如果是JNI_TRUE,那么JNI_CreateJavaVM函数会忽略掉所有
无效选项以"-X"或"_"开头的。如果是JNI_FALSE,那么JNI_CreateJavaVM函数遇到无效选项时会返回一个JNI_ERR.
编译main.cpp:
main.cpp包含了jni.h,后者又包含了jni_md.h,这两个文件的路径分别如下:
/usr/lib/jvm/java-6-sun-1.6.0.26/include
/usr/lib/jvm/java-6-sun-1.6.0.26/include/linux
要使用JNI_CreateJavaVM函数,需要链接libjvm.so动态库,路径如下:
/usr/lib/jvm/java-1.5.0-sun-1.5.0.15/jre/lib/i386/server
因此编译方法如下: $ g++ -I/usr/lib/jvm/java-6-sun-1.6.0.26/include -I/usr/lib/jvm/java-6-sun-1.6.0.26/include/linux -L/usr/lib/jvm/java-1.5.0-sun-1.5.0.15/jre/lib/i386/server/ -ljvm main.cpp
在同级目录创建一个java文件HelloWorld.java:
package com.hello; public class HelloWorld{ public static void main(String[] args){ System.out.println("Hello world from java!"); } }编译此文件:
$ javac -d . HelloWorld.java
接下来要运行a.out还需一个步骤:修改当前终端的动态链接库环境变量让它能找到libjvm.so.
$ export LD_LIBRARY_PATH=/usr/lib/jvm/java-1.5.0-sun-1.5.0.15/jre/lib/i386/server:$LD_LIBRARY_PATH
然后运行 ./a.out
会出现输出:
创建jvm成功!
成功找到类!
Hello world from java!
-------------------
启动了java虚拟机以后,如果使用ps命令查看运行的进程,就会发现java虚拟机仍然运行在a.out可执行文件被加载起来的进程中,而且只有java虚拟机退出了,才会继续执行main.cpp剩余的代码。