JNIEnv说明

JavaVM接口

第一种方式,在加载动态连接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(若是定义了该函数)。第一个参数会传入JavaVM指针。c++

第二种方式,在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)能够获得JavaVM指针。jvm

两种状况下,均可以用全局变量,好比JavaVM* g_jvm来保存得到的指针以便在任意上下文中使用。函数

Android系统是利用第二种方式Invocation interface来建立JVM的。this

JNIEnv接口

须要强调的是JNIEnv是跟线程相关的。spa

在native method中,JNIEnv做为第一个参数传入。那么在JNIEnv不做为参数传入的时候,该如何得到它?线程

JNI提供了两个函数:设计

(*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL)指针

 (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)code

 两个函数都利用JavaVM接口得到JNIEnv接口,上面已经讲到如何得到JavaVM接口。orm

 JNI规范也说明,能够将得到JNIEnv封装成一个函数。

1
2
3
4
5
6
JNIEnv* JNU_GetEnv()
{
             JNIEnv* env;
             (*g_jvm)->GetEnv(g_jvm, (         void         **)&env, JNI_VERSION_1_2);
             return          env;
}

Java经过JNI机制调用c/c++写的native程序。c/c++开发的native程序须要遵循必定的JNI规范,下面的例子就是一个JNI函数声明:

JNIEXPORT jint JNICALL Java_jnitest_MyTest_test
  (JNIEnv 
* env, jobject obj, jint arg0);

JVM负责从Java Stack转入C/C++ Native Stack。当Java进入JNI调用,除了函数自己的参数(arg0),会多出两个参数:JNIEnv指针和jobject指针。
JNIEnv指针是JVM建立的,用于Native的c/c++方法操纵Java执行栈中的数据,好比Java Class, Java Method等。
首先,JNI对于JNIEnv的使用, 提供了两种语法: c语法以及c++语法,以下:
c语法:

jsize len = (*env)->GetArrayLength(env,array);

c++语法:

jsize len =env->GetArrayLength(array);

(注:因为C语言并不支持对象的概念,因此C语法中须要把env做为第一个参数传入,相似于C++的隐式参数this指针).

对于JNIEnv *env来讲,在C中调用:

(*env)->NewStringUTF(env, "Hello from JNI!");

而在C++中若是按照上述调用则会发生'base operand of '->' has non-pointer type '_JNIEnv''错误,须要以下调用:

env->NewStringUTF("Hello from JNI!");

缘由:参见jni.h中对于JNIEnv的定义:

#if defined(__cplusplus)

typedef _JNIEnv JNIEnv;

#else

typedef const struct JNINativeInterface* JNIEnv;

#endif
另外: JNIEnv有几个设计的原则:
第1、JNIEnv指针被设计成了Thread Local Storage(TLS)变量,也就是说每个Thread, JNIEnv变量都有独立的Copy。你不能把Thead#1使用的JNIEnv传给Thread#2使用。

第 2、JNIEnv中定义了一组函数指针,c/c++ Native程序是经过这些函数指针操纵Java数据。这样设计的好处是:你的c/c++ 程序不须要依赖任何函数库,或者DLL。因为JVM可能由不一样的厂商实现,不一样厂商有本身不一样的JNI实现,若是要求这些厂商暴露约定好的一些头文件和 库,这不是灵活的设计。
并且使用函数指针表的另一个好处是: JVM能够根据启动参数动态替换JNI实现。

相关文章
相关标签/搜索