本文基於JNI函數接口的常用用法整理了AndroidStudio工程實例,便於大家學習和記憶。
這篇文章是基於參考資料[1]中第十三章"JNI Functions"整理的,TestCode涵蓋了以下用法:
[0 整理思路]
由參考資料[1]中第十三章"JNI Functions"中,我們可以將jni functions大致分爲如下四大類:
1 由VM直接導出的調用接口函數
2 JavaVM接口
3 Native庫中定義的函數
4 JNIEnv接口
我們將按照如上順序,學習jni functions.
[1 由VM直接導出的調用接口函數]
注意:這三個接口只是由VM爲JNI導出的三個符號,一般不用用戶調用!
1 JNI_GetDefaultJavaVMInitArgs
jint JNI_GetDefaultJavaVMInitArgs(void*);
返回Java虛擬機實現的默認配置。
2 JNI_CreateJavaVM
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
加載並初始化一個虛擬機實例。
3 JNI_GetCreatedJavaVMs
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
[2 JavaVM接口]
1 DestroyJavaVM
jint DestroyJavaVM()
卸載JavaVM並釋放它的資源。
2 AttachCurrentThread
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
綁定當前線程(一般是子線程)到一個虛擬機實例。
3 DetachCurrentThread
jint DetachCurrentThread()
使得當前線程脫離一個虛擬機實例。
4 GetEnv
jint GetEnv(void** env, jint version)
(1)用來檢查當前線程是否綁定到給定的虛擬機實例,env設置爲NULL,返回錯誤JNI_EDETACHED.
(2)用來獲取其他接口,如JNIEnv的interfaces.
[3 Native庫中定義的函數]
1 JNI_OnLoad
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
當虛擬機加載一個native庫時,調用JNI_OnLoad接口。即System.loadLibrary會調用JNI_OnLoad接口。獲取JavaVM對象。
2 JNI_OnUnload
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
當虛擬機卸載一個native庫時,調用JNI_OnUnload接口。
[4 JNIEnv接口]
(1)Version Information
1 GetVersion
jint GetVersion()
返回JNIEnv interface的版本號
(2)Class and Interface Operations
1 DefineClass
jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen)
從一個jbyte* buffer裏定義一個類
2 FindClass
jclass FindClass(const char* name)
返回一個類的引用
3 GetSuperclass
jclass GetSuperclass(jclass clazz)
返回基類,除了java.lang.Object類(因爲java.lang.Object是最基類)
(3)Exceptions
1 FatalError
void FatalError(const char* msg)
打印信息並終止當前虛擬機實例
2 ExceptionCheck
jboolean ExceptionCheck()
檢查當前線程是否發生了異常,若有異常返回JNI_TRUE,否則返回JNI_FALSE
3 ExceptionDescribe
void ExceptionDescribe()
JNI層打印異常的堆棧信息!!!
4 ExceptionClear
void ExceptionClear()
清除異常堆棧信息
5 Throw and ThrowNew
jint Throw(jthrowable obj)
丟棄一個現有的異常對象,在當前線程觸發一個新的異常
jint ThrowNew(jclass clazz, const char* message)
在當前線程觸發一個異常,並自定義輸出異常信息
6 ExceptionOccurred
jthrowable ExceptionOccurred()
檢查當前線程是否發生了異常,若有異常返回該異常的引用,否則返回NULL
(4)Global and Local References
1 NewGlobalRef,DeleteGlobalRef
jobject NewGlobalRef(jobject obj)
void DeleteGlobalRef(jobject globalRef)
(1)創建:
調用NewGlobalRef,基於全局引用,弱全局引用,局部引用創建
(2)會阻止GC回收所引用的對象:
只有手動釋放全局引用後,垃圾回收機制纔可以回收
(3)能跨函數使用,能跨線程使用:
(4)釋放:
(i)自動釋放:無自動釋放
(ii)手動釋放:(*env)->DeleteGlobalRef(env,g_cls_string)
2 NewWeakGlobalRef,DeleteWeakGlobalRef
jweak NewWeakGlobalRef(jobject obj)
void DeleteWeakGlobalRef(jweak obj)
(1)創建:
調用NewWeakGlobalRef,基於局部引用或全局引用創建
(2)不會阻止GC回收所引用的對象:
GC認爲應該回收它的時候(比如內存緊張的時候)會進行自動回收,從而釋放全局弱引用
(3)能跨函數使用,能跨線程使用:
(4)釋放:
(i)自動釋放:在GC認爲應該回收它的時候(比如內存緊張的時候)會進行自動回收,釋放全局弱引用
(ii)手動釋放:(*env)->DeleteWeakGlobalRef(env,g_cls_string)
3 NewLocalRef,DeleteLocalRef
jobject NewLocalRef(jobject ref)
void DeleteLocalRef(jobject localRef)
(1)創建
調用NewLocalRef或其他JNI接口(FindClass、NewObject、NewCharArray)創建。
(2)會阻止GC回收所引用的對象
只有釋放局部引用後GC纔可以回收
(3)不能跨函數使用,不能跨線程使用
(4)釋放
(i)自動釋放:函數返回後局部引用的當前對象會被GC自動釋放,所以局部引用是有內存溢出的風險的!!!
例如:在函數體內有循環,循環裏New局部引用會出現內存溢出!!!
JNIEXPORT void JNICALL f(JNIEnv *env,jobject obj){
for(int i=0;i<len;i++){
jobject strLocalref = env->NewLocalRef(strObject);
...
//如果沒有手動釋放strLocalref ,只有最後一個strLocalref 會被GC自動釋放,其餘strLocalref會出現native內存泄漏
//env->DeleteLocalRef(strLocalref );
}
}
(ii)手動釋放:(*env)->DeleteLocalRef(env,local_ref)
(5)Object Operations
1 GetObjectClass
jclass GetObjectClass(jobject obj)
返回一個對象對應的類
2 IsInstanceOf
jboolean IsInstanceOf(jobject obj, jclass clazz)
檢查一個對象是否是一個類的實例
3 NewObject
jobject NewObject(jclass clazz, jmethodID methodID, ...)
分配對象空間並構造該對象
4 AllocObject
jobject AllocObject(jclass clazz)
分配一個未初始化的對象
(6)Instance Field Access
1 GetFieldID
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
獲取類的成員域ID
2 Get<Type>Field,Set<Type>Field
jint GetIntField(jobject obj, jfieldID fieldID)
根據成員域ID獲取類成員
void SetIntField(jobject obj, jfieldID fieldID, jint value)
根據成員域ID設置類成員
(7)Static Field Access
1 GetStaticFieldID
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
獲取類的靜態成員域ID
2 GetStatic<Type>Field,SetStatic<Type>Field
jint GetStaticIntField(jclass clazz, jfieldID fieldID)
根據成員域ID獲取類靜態成員
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
根據成員域ID設置類靜態成員
(8)Instance Method Calls
1 GetMethodID
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
獲取一個類方法的方法ID
2 Call<Type>Method
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
調用類方法
(9)Static Method Calls
1 GetStaticMethodID
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
獲取一個類靜態方法的方法ID
2 CallStatic<Type>Method
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
調用類靜態方法
(10)String Operations
char*(1字節)~UTF-8字符串
jchar*(2字節)~Unicode字符串
1 NewString
jstring NewString(const jchar* unicodeChars, jsize len)
通過jchar*創建一個jstring對象
2 NewStringUTF
jstring NewStringUTF(const char* bytes)
通過char*創建一個jstring對象
3 GetStringLength
jsize GetStringLength(jstring string)
返回jstring的長度
4 GetStringUTFLength
jsize GetStringUTFLength(jstring string)
返回jstring的長度
5 GetStringChars/ReleaseStringChars
const jchar* GetStringChars(jstring string, jboolean* isCopy)
通過jstring獲取jchar*指針
void ReleaseStringChars(jstring string, const jchar* chars)
釋放jstring對應的jchar*指針
6 GetStringUTFChars/ReleaseStringUTFChars
const char* GetStringUTFChars(jstring string, jboolean* isCopy)
通過jstring獲取char*指針
void ReleaseStringUTFChars(jstring string, const char* utf)
釋放jstring對應的char*指針
7 GetStringCritical/ReleaseStringCritical
const jchar* GetStringCritical(jstring string, jboolean* isCopy)
通過jstring獲取jchar*指針
void ReleaseStringCritical(jstring string, const jchar* carray)
釋放jstring對應的jchar*指針
8 GetStringRegion/GetStringUTFRegion
void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
將jstring對象拷貝到jchar*內存
void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
將jstring對象拷貝到char*內存
(11)Array Operations
1 GetArrayLength
jsize GetArrayLength(jarray array)
返回數組元素個數
2 NewObjectArray
jobjectArray NewObjectArray(jsize length, jclass elementClass,jobject initialElement)
創建對象數組
3 New<Type>Array
jintArray NewIntArray(jsize length)
創建基本類型數組(int,char...)
4 GetObjectArrayElement/SetObjectArrayElement
jobject GetObjectArrayElement(jobjectArray array, jsize index)
獲取對象數組元素
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
設置對象數組元素
5 Get<Type>ArrayElements/Release<Type>ArrayElements
jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
獲取基本類型數組內存指針
void ReleaseIntArrayElements(jintArray array, jint* elems,jint mode)
釋放基本類型數組內存
6 Get<Type>ArrayRegion/Set<Type>ArrayRegion
void GetIntArrayRegion(jintArray array, jsize start, jsize len,jint* buf)
拷貝jintArray到jint*
void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf)
拷貝const jint*到jintArray
(12)Native Method Registration
1 RegisterNatives/UnregisterNatives
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
註冊native方法
jint UnregisterNatives(jclass clazz)
註銷native方法
(13)Monitor Operations
1 MonitorEnter
jint MonitorEnter(jobject obj)
進入臨界區
2 MonitorExit
jint MonitorExit(jobject obj)
退出臨界區
(14)JavaVM Interface
1 GetJavaVM
jint GetJavaVM(JavaVM** vm)
獲取當前虛擬機JavaVM指針
[5 參考資料]
[1]The Java™ Native Interface Programmer’s Guide and Specification.pdf
[2]JNI/NDK開發指南
https://blog.csdn.net/xyang81/article/category/2759987