添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
JNI 中局部引用和全局引用

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