jni如何判斷兩個jobject是否爲同一個java對象

  jni如何判斷兩個jobject是否爲同一個java對象

 

    jni開發時有時候需要將java對象緩存到native層,方便native層通過jni的反射方法進行回調操作。通常我們會將回調接口callback在native層存放爲global reference全局引用,熟悉jni開發的都知道,jni傳入到native 層的jobject生命週期僅僅是函數的生命週期,當jni函數返回後對應的jobject對象就會失效,不能再操作,所以就必須申請爲global reference。

NewGlobalRef 將變量申請爲全局引用,此時java虛擬機會保留jobject所指向的對象防止被垃圾回收器回收。 DeleteGlobalRef 釋放全局引用,允許java虛擬機回收該引用指向的java對象,對於不需要使用的global reference必須調用該方法,否則會引起java虛擬機內存泄漏。

      既然global reference的實質是指向java對象,那麼我們在將某個變量聲明爲global reference時如何判斷該jobject是否已經是當前設置的global reference呢。經過一番查詢,看到了一下方法

IsSameObject 如果兩個jobject指向同一個java 對象那麼返回true,否則返回false。

 

既然有方法了,接下來就是驗證工作了。

Hello.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */

#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Hello
 * Method:    native_setObjectToGlobalRef
 * Signature: (LHello/Book;)V
 */
JNIEXPORT void JNICALL Java_Hello_native_1setObjectToGlobalRef
  (JNIEnv *, jclass, jobject);

/*
 * Class:     Hello
 * Method:    native_release
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Hello_native_1release
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

Hello.cpp

#ifdef __cplusplus
extern "C" {
#endif

#include <cstdio>
#include "Hello.h"

// 全局變量,用於存儲全局引用的
jobject g_book_ref = NULL; 

/*
 * Class:     Hello
 * Method:    native_setObjectToGlobalRef
 * Signature: (LHello/Book;)V
 */
JNIEXPORT void JNICALL Java_Hello_native_1setObjectToGlobalRef
  (JNIEnv *env, jclass jclazz, jobject jbook) {

	// 如果輸入參數爲null,那麼不需要繼續操作
	if (NULL == jbook) {
		printf("native_setObjectToGlobalRef jbook is NUll and g_book_ref is NOT NULL. we need to delete reference of old g_book_ref\n");
		if (NULL != g_book_ref) {
            env->DeleteGlobalRef(g_book_ref);  // 刪除老的全局引用
		    g_book_ref = NULL;
        }
		return;
	}

   // 如果全局引用已經設置了,那麼需要判斷jni函數參數的jbook是否與當前的全局引用指向同一個java對象
   if (NULL != g_book_ref) {
        // 如果指向同一個對象,那麼不需要再次爲該jbook申請全局引用
		if (env->IsSameObject(g_book_ref, jbook)) {
			printf("native_setObjectToGlobalRef isSameObject: true, g_book_ref: %p, jbook: %p\n", g_book_ref, jbook);
		} else {
            // 如果指向不同對象,那麼先釋放老的全局引用,再爲jbook申請全局引用
			printf("native_setObjectToGlobalRef isSameObject: false\n");
			printf("native_setObjectToGlobalRef g_book_ref and jbook is not same object. we need to delete reference of old g_book_ref\n");
			env->DeleteGlobalRef(g_book_ref);
			printf("native_setObjectToGlobalRef create global reference to g_book_ref\n");
			g_book_ref = env->NewGlobalRef(jbook);
		}
	} else {
		printf("native_setObjectToGlobalRef g_book_ref is NULL\n");
		printf("native_setObjectToGlobalRef create global reference to g_book_ref\n");
		g_book_ref = env->NewGlobalRef(jbook);
	}
}

/*
 * Class:     Hello
 * Method:    native_release
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Hello_native_1release
  (JNIEnv *env, jclass jclazz) {
    printf("native_release g_book_ref: %p\n", g_book_ref);
	if (NULL != g_book_ref) {
		env->DeleteGlobalRef(g_book_ref);
		g_book_ref = NULL;
	}
}


#ifdef __cplusplus
}
#endif


Hello.java

public class Hello {
	static {
		try {
			String sysName = System.getProperty("os.name");
			if (sysName.contains("Linux")) {
		        System.load(System.getProperty("user.dir") + "/libHello.so");
			} else if (sysName.contains("Drawin") || sysName.contains("Mac")) {
		        System.load(System.getProperty("user.dir") + "/libHello.dylib");
			}
		} catch (java.lang.UnsatisfiedLinkError e) {
			e.printStackTrace();
			System.err.println("load so failed.");
			System.exit(1);
		}
	}

	static class Book {
		public String name;

		public Book() {}
		public Book(String name) {
			this.name = name;
		}
	}

	public native static void native_setObjectToGlobalRef(Book book);
	public native static void native_release();

	public static void main(String args[]) {
		Book b1 = new Book();
		Book b2 = new Book();
		System.out.println("set b1");
		native_setObjectToGlobalRef(b1);
		System.out.println("set b2");
		native_setObjectToGlobalRef(b2);
		System.out.println("##set b2");
		native_setObjectToGlobalRef(b2);
		System.out.println("##set null");
		native_setObjectToGlobalRef(null);
		native_release();
	}
}

 

makefile

UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
main:
	gcc -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/darwin" Hello.cpp -dynamiclib -o libHello.dylib
endif

ifeq ($(UNAME_S),Linux)
main:
	gcc -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" Hello.cpp -shared -fPIC -o libHello.so
endif

 

運行命令

$ make

$ javac Hello.java

 

運行程序

$ java -Djava.library.path=".:${JAVA_HOME}" Hello

輸出結果

set b1
native_setObjectToGlobalRef g_book_ref is NULL
native_setObjectToGlobalRef create global reference to g_book_ref
set b2
native_setObjectToGlobalRef isSameObject: false
native_setObjectToGlobalRef g_book_ref and jbook is not same object. we need to delete reference of old g_book_ref
native_setObjectToGlobalRef create global reference to g_book_ref
##set b2
native_setObjectToGlobalRef isSameObject: true, g_book_ref: 0x7fb16ae08e90, jbook: 0x70000ce4a900
##set null
native_setObjectToGlobalRef jbook is NUll and g_book_ref is NOT NULL. we need to delete reference of old g_book_ref
native_release g_book_ref: 0x0

1. set b1時

因爲是首次設置,此時native的g_book_ref還是null,沒有被指向java對象,所以需要用全局引用爲jbook保存java對象引用;

2. set b2時

isSameObject返回false,證明g_book_ref和jbook指向的對象不一樣(此時jbook指向b2,而g_book_ref指向步驟1設置的b1),所以需要先刪除老的b1全局引用再爲b2申請全局引用;

3. 再次設置b2時

isSameObject返回true,證明g_book_ref和jbook指向的對象一樣(此時jbook和g_book_ref都指向b2,注意兩者的native地址不一樣),爲了節省操作,所以不需要在爲jbook申請全局引用;

4. 設置爲null

因爲jbook爲null,所以需要釋放全局引用g_book_ref。

5. native釋放

在Book native釋放時因爲步驟4已經提前釋放了全局引用,所以不需要再釋放了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章