一、jni调用java对象
JNI提供的功能之一是在本地代码中使用Java对象。包括:创建一个java类对象和通过函数传递一个java对象。创建一个java类对象,首先需要得到得到使用FindClass/GetObjectClass函数得到该类,然后使用GetMethodID方法得到该类的方法id,然后调用该函数。 Java 和 Native 代码之间函数调用时,如果是简单类型,也就是内置类型,比如 int, char 等是值传递(pass by value),而其它 Java 对象都是引用传递(pass by reference),这些对象引用由 JVM 传给 Native 代码。
在本地方法中调用Java对象的方法的步骤:
- 1)获取你需要访问的Java对象的类 FindClass通过传java中完整的类名来查找java的class GetObjectClass通过传入jni中的一个java的引用来获取该引用的类型。 他们之间的区别是,前者要求你必须知道完整的类名,后者要求在Jni有一个类的引用。
- 2)获取MethodID,调用方法 GetMethodID 得到一个实例的方法的ID GetStaticMethodID 得到一个静态方法的ID
- 3)获取对象的属性 GetFieldID 得到一个实例的域的ID GetStaticFieldID 得到一个静态的域的ID JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。
二、jni中引用的java对象的生命周期
Java对象做为引用被传递到本地方法中,所有这些Java对象的引用都有一个共同的父类型jobject(相当于java中的 Object类是所有类的父类一样)。 这些对象引用都有其生命周期。在JNI中对Java对象的引用根据生命周期分为:全局引用,局部引用、弱全局引用
1、Local Reference 本地引用, 函数调用时传入jobject或者jni函数创建的jobejct,都是本地引用. 其特点就是一旦JNI层函数返回,jobject就被垃圾回收掉,所以需要注意其生命周期。可以强制调用DeleteLocalRef进行立即回收。 jstring pathStr = env->NewStringUTF(path) …. env->DeleteLocalRef(pathStr);
2、Global Reference 全局引用 ,这种对象如不主动释放,它永远都不会被垃圾回收 创建: env->NewGlobalRef(obj); 释放: env->DeleteGlobalRef(obj) 若要在某个 Native 代码返回后,还希望能继续使用 JVM 提供的参数, 或者是过程中调用 JNI 函数的返回值(比如 g_mid), 则将该对象设为 global reference,以后只能使用这个 global reference;若不是一个 jobject,则无需这么做。
3、Weak Global Reference 弱全局引用 一种特殊的 Global Reference ,在运行过程中可能被垃圾回收掉,所以使用时请务必注意其生命周期及随时可能被垃圾回收掉,比如内存不足时。 使用前可以利用JNIEnv的 IsSameObject 进行判定它是否被回收 env->IsSameObject(obj1,obj2);
三、本地线程中调用java对象
- 问题1: JNIEnv是一个线程相关的变量 JNIEnv 对于每个 thread 而言是唯一的 JNIEnv *env指针不可以为多个线程共用
- 解决办法: 但是java虚拟机的JavaVM指针是整个jvm公用的,我们可以通过JavaVM来得到当前线程的JNIEnv指针. 可以使用javaAttachThread保证取得当前线程的Jni环境变量 static JavaVM *gs_jvm=NULL; gs_jvm->AttachCurrentThread((void **)&env, NULL);//附加当前线程到一个Java虚拟机 jclass cls = env->GetObjectClass(gs_object); jfieldID fieldPtr = env->GetFieldID(cls,“value”,“I”);
- 问题2: 不能直接保存一个线程中的jobject指针到全局变量中,然后在另外一个线程中使用它。
- 解决办法: 用env->NewGlobalRef创建一个全局变量,将传入的obj(局部变量)保存到全局变量中,其他线程可以使用这个全局变量来操纵这个java对象 注意:若不是一个 jobject,则不需要这么做。如: jclass 是由 jobject public 继承而来的子类,所以它当然是一个 jobject,需要创建一个 global reference 以便日后使用。 而 jmethodID/jfieldID 与 jobject 没有继承关系,它不是一个 jobject,只是个整数,所以不存在被释放与否的问题,可保存后直接使用。 static jobject gs_object=NULL; JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj) { env->GetJavaVM(&gs_jvm); //保存到全局变量中JVM //直接赋值obj到全局变量是不行的,应该调用以下函数: gs_object=env->NewGlobalRef(obj); }
jni部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
|
需要全部源码的,可以打开这个链接下载 http://download.csdn.net/detail/mfcai_blog/5772377