JNI 中局部引用和全局引用
在上一篇中介绍了JNI中创建对象数组。本文是JNI系列的第十一篇,介绍JNI中的如何调在Native代码调用Java中的方法和静态方法。
系列文章的大纲如下:
- JNI 简介
- JNI 基本类型
- JNI String
- JNI 数组
- JNI 实例变量
- JNI 静态变量
- JNI 回调实例方法与静态方法
- JNI 调用Java中的super.method()
- JNI 中创建对象
- JNI 中创建对象数组
- JNI 中局部引用和全局引用
- JNI 动态注册
- 使用Android NDK编译Android的Native库
JNI 中局部引用和全局引用
在Native方法中,经常使用
FindClass()
、
GetMethodID()
、
GetFieldID()
来获取
jclass
、
jmethodID
、
jfieldID
。
这些方法的调用是有成本的,应该将它们缓存起来以后使用。
-
在前面的文章当中使用的方式都是以局部引用的方式获取的对象,它们在Native方法中创建,并在退出Native
方法时销毁。也可以使用DeleteLocalRef()
显示的销毁局部引用。 -
全局的引用需要使用
NewGlobalRef()
从局部引用创建,使用DeleteGlobalRef()
进行销毁。
// 创建对象obj一个新的全局引用
jobject NewGlobalRef(JNIEnv *env, jobject obj);
// 删除globalRef的全局引用
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
// 删除localRef的局部引用
void DeleteLocalRef(JNIEnv *env, jobject localRef);
通过下面的实例来加深一下理解。
实例
我们来看一个实例。
package myjni;
public class JNIReference {
static {
System.loadLibrary("hello");
public native Integer getInteger(int number);
public native Integer getInteger2(int number);
public static void main(String[] args) {
JNIReference obj = new JNIReference();
System.out.println("In Java, " + obj.getInteger(1));
System.out.println("In Java, " + obj.getInteger(2));
System.out.println("In Java, " + obj.getInteger2(3));
}
如果还不清楚如果生成头文件请参考 JNI简介 。
生成头文件
myjni_JNIReference.h.h
的签名为:
/*
* Class: myjni_JNIReference
* Method: getInteger
* Signature: (I)Ljava/lang/Integer;
JNIEXPORT jobject JNICALL Java_myjni_JNIReference_getInteger
(JNIEnv *, jobject, jint);
* Class: myjni_JNIReference
* Method: getInteger2
* Signature: (I)Ljava/lang/Integer;
JNIEXPORT jobject JNICALL Java_myjni_JNIReference_getInteger2
(JNIEnv *, jobject, jint);
实现函数
Java_myjni_JNIReference_getInteger
和
Java_myjni_JNIReference_getInteger2
:
#include "myjni_JNIReference.h"
#include <iostream>
static jclass classInteger = nullptr;
static jmethodID midIntegerInit = nullptr;
jobject CreateInteger(JNIEnv *env, jobject obj, jint number) {
// Get Integer reference
if (classInteger == nullptr) {
std::cout << "find class java.lang.Integer\n";
classInteger = env->FindClass("java/lang/Integer");
if (classInteger == nullptr) {
std::cout << "find class java.lang.Integer failed\n";
return nullptr;
// Get method ID reference
if (midIntegerInit == nullptr) {
std::cout << "get Integer's constructor method id\n";
midIntegerInit = env->GetMethodID(classInteger, "<init>", "(I)V");
if (midIntegerInit == nullptr) {
std::cout << "get Integer's constructor method id failed\n";
return nullptr;
jobject result = env->NewObject(classInteger, midIntegerInit, number);
std::cout << "In C++, constructed java.lang.Integer with number " << number << std::endl;
return result;
JNIEXPORT jobject JNICALL Java_myjni_JNIReference_getInteger(JNIEnv *env, jobject obj, jint number) {
return CreateInteger(env, obj, number);
JNIEXPORT jobject JNICALL Java_myjni_JNIReference_getInteger2(JNIEnv *env, jobject obj, jint number) {
return CreateInteger(env, obj, number);
}
编译生成动态库,并运行Java程序得到输出:
find class java.lang.Integer
get Integer's constructor method id
In C++, constructed java.lang.Integer with number 1
In Java, 1
# A fatal error has been detected by the Java Runtime Environment:
# ...
有崩溃发生,这里面把局部引用赋值给了全局静态变量,局部变量在第一次Native调用之后被自动释放导致第二次Native调用时
发生崩溃。
修改
CreateInteger()
的实例,创建全局引用:
// Get Integer reference
if (classInteger == nullptr) {
std::cout << "find class java.lang.Integer\n";
classInteger = env->FindClass("java/lang/Integer");
classInteger = (jclass)env->NewGlobalRef(classInteger);
if (classInteger == nullptr) {
std::cout << "find class java.lang.Integer failed\n";
return nullptr;
}
运行结果如下:
find class java.lang.Integer
get Integer's constructor method id