Local and global references in JNI

Keywords: Java Android JNI

Local and global references in JNI

In the previous article, I introduced creating object arrays in JNI. This article is the eleventh in the JNI series. It introduces how to call Java methods and static methods in Native code in JNI.

The outline of the series is as follows:

Local and global references in JNI

In Native methods, FindClass(), GetMethodID(), GetFieldID() are often used to obtain jclass, jmethodidid, and jfieldID.
Calls to these methods are costly and should be cached for later use.

  • The methods used in the previous articles are objects obtained by local reference. They are created in the Native method and exit the Native method
    Method. You can also destroy local references using the displayed by DeleteLocalRef().
  • Global references need to be created from local references using NewGlobalRef() and destroyed using DeleteGlobalRef().
// Create obj a new global reference to the object
jobject NewGlobalRef(JNIEnv *env, jobject obj);

// Delete global reference of globalRef
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

// Delete local reference of localRef
void DeleteLocalRef(JNIEnv *env, jobject localRef);

Use the following examples to deepen your understanding.

example

Let's look at an example.

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));
    }
}

If it is not clear, please refer to Introduction to JNI.

Generate header file myjni_ The signature of jnireference. H.H is:

/*
 * 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);

Implement function Java_myjni_JNIReference_getInteger and 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);
}

Compile and generate the dynamic library, and run the Java program to get the output:

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:
# ...

There is a crash, in which the local reference is assigned to the global static variable. The local variable is automatically released after the first Native call, resulting in the second Native call
A crash occurred.

Modify the instance of CreateInteger() to create a global reference:

  // 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;
    }
  }

The operation results are as follows:

find class java.lang.Integer
get Integer's constructor method id
In C++, constructed java.lang.Integer with number 1
In Java, 1
In C++, constructed java.lang.Integer with number 2
In Java, 2
In C++, constructed java.lang.Integer with number 3
In Java, 3

vx search: geek Furzoom, pay attention to obtaining first-hand information.

The end of this article.

Posted by Nomaad on Tue, 23 Nov 2021 17:24:54 -0800