1. 程式人生 > >Java中JNI的使用(下)

Java中JNI的使用(下)

將不 scribe gre 一個數 lai num 開頭 數組長度 jclass

數組的操作

數組是一個很常用的數據類型,在但是在 JNI 中並不能直接操作 jni 數組(比如 jshortArray、jfloatArray)。使用方法是:

  1. 獲取數組長度:jsize GetArrayLength(jarray array)
  2. 創建新數組: ArrayType New<PrimitiveType>Array(jsize length);
  3. 通過JNI數組獲取一個C/C++數組:<type>* Get<type>ArrayElements(jshortArray array, jboolean *isCopy)
  4. 指定原數組的範圍獲取一個C/C++數組(該方法只針對於原始數據數組,不包括Object數組):void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);
  5. 設置數組元素:void Set<type>ArrayRegion(jshortArray array, jsize start, jsize len,const <type> *buf)。again,如果是Object數組需要使用:void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
  6. 使用完之後,釋放數組:void Release<type>ArrayElements(jshortArray array, jshort *elems, jint mode)

有點要說明的:

1、上面的3中的 isCopy:當你調用 getArrayElements 時 JVM(Runtime)可以直接返回數組的原始指針,或者是 copy 一份,返回給你,這是由 JVM 決定的。所以 isCopy 就是用來記錄這個的。他的值是 JNI_TURE 或者 JNI_FALSE

2、6釋放數組。一定要釋放你所獲得數組。其中有一個mode參數,其有三個可選值,分別表示:

  • 0
    • 原始數組:允許原數組被垃圾回收。
    • copy: 數據會從get返回的buffer copy回去,同時buffer也會被釋放。
  • JNI_COMMIT
    • 原始數組:什麽也不做
    • copy: 數據會從get返回的buffer copy回去,同時buffer不會被釋放。
  • JNI_ABORT
    • 原始數組:允許原數組被垃圾回收。之前由JNI_COMMIT提交的對數組的修改將得以保留。
    • copy: buffer會被釋放,同時buffer中的修改將不會copy回數組!

關於引用與垃圾回收

比如上面有個方法傳了一個 jobject 進來,然後我把她保存下來,方便以後使用。這樣做是不行噠!因為他是一個 LocalReference,所以不能保證 jobject 指向的真正的實例不被回收。也就是說有可能你用的時候那個指針已經是個野指針的。然後你的程序就直接 Segment Fault 了,呵呵。

在JNI中提供了三種類型的引用:

  1. Local Reference:即本地引用。在JNI層的函數,所有非全局引用對象都是Local Reference, 它包括函數調用是傳入的jobject和JNI成函數創建的jobject。Local Reference的特點是一旦JNI層的函數返回,這些jobject就可能被垃圾回收。
  2. Glocal Reference:全局引用,這些對象不會主動釋放,永遠不會被垃圾回收。
  3. Weak Glocal Reference:弱全局引用,一種特殊的Global Reference,在運行過程中有可能被垃圾回收。所以使用之前需要使用jboolean IsSameObject(jobject obj1, jobject obj2)判斷它是否已被回收。

Glocal Reference:
1. 創建:jobject NewGlobalRef(jobject lobj);
2. 釋放:void DeleteGlobalRef(jobject gref);

Local Reference:
LocalReference也有一個釋放的函數:void DeleteLocalRef(jobject obj),他會立即釋放Local Reference。 這個方法可能略顯多余,其實也是有它的用處的。剛才說Local Reference會再函數返回後釋放掉,但是假如函數返回前就有很多引用占了很多內存,最好函數內就盡早釋放不必要的內存。

關於JNI_OnLoad

開頭提到 JNI_OnLoad 是 Java1.2 中新增加的方法,對應的還有一個 JNI_OnUnload,分別是動態庫被 JVM 加載、卸載的時候調用的函數。有點類似於 Windows 裏的 DllMain。
前面提到的實現對應 native 的方法是實現 javah 生成的頭文件中定義的方法,這樣有幾個弊端:

  1. 函數名太長。很長,相當長。
  2. 函數會被導出,也就誰說可以在動態庫的導出函數表裏面找到這些函數。這將有利於別人對動態庫的逆向工程,因此帶來安全問題。

現在有了JNI_OnLoad,情況好多了。你不光能在其中完成動態註冊 native 函數的工作還可以完成一些初始化工作。Java 對應的有了 jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,jint nMethods)函數。參數分別是:

  • jclass clazz,於native層對應的java class
  • const JNINativeMethod *methods這是一個數組,數組的元素是JNI定義的一個結構體JNINativeMethod
  • 上面的數組的長度

JNINativeMethod:代碼中的定義如下

1 2 3 4 5 6 7 8 9 10 /* * used in RegisterNatives to describe native method name, signature, * and function pointer. */ typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod;

所以他有三個字段,分別是

技術分享圖片

於是現在你可以不用導出 native 函數了,而且可以隨意給函數命名,唯一要保證的是參數及返回值的統一。然後需要一個 const JNINativeMethod *methods 數組來完成映射工作。

Java中JNI的使用(下)