定義: 至關於一個jni上下文對象。java
做用: 經過JNIEnv的指針可以對Java端的代碼進行操做:編程
a.建立Java對象.數組
jstring str = (env).NewStringUTF("終端研發部");
jclass jclazz = (env).GetObjectClass(obj);緩存
b.調用Java對象的方法。bash
jclz = (*env)->FindClass(env, "java/lang/String");
jdouble doub = (*env).GetStaticDoubleField()複製代碼
c.獲取及設置Java對象的屬性。微信
jintArray arrayResult = NULL;
jclass jclazz = (*env).GetObjectClass(obj);
jint * elements =(*env).GetIntArrayElements(array,NULL);複製代碼
Get/Set[Static]
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;複製代碼
[cpp] view plain copy
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};複製代碼
JNIEXPORT void JNICALL Java_com_jue_testnative_TestNative1_hello(JNIEnv *, jobject);
這裏是jobject指代的在Java中調用native方法的java類實例複製代碼
a. jclass FindClass(const char *name)
b. jclass GetObjectClass(jobject obj)複製代碼
注意: 會在ClassPath下面尋找類。須要傳入完整類名,注意包與包之間用’/'。函數
jclass class_str = env->FindClass("java/lang/String");複製代碼
在natvie方法中獲取/設置字段的值,或者方法調用,須要先獲取相應的field/method的ID工具
jfieldID GetFieldID(jclass clazz, const char name, const char sig) ui
jmethodID GetMethodID(jclass clazz, const char name, const char sig)
注意sig用來處理函數重載引發的不肯定。
而後經過ID獲取
jobject GetObjectField(jobject obj, jfieldID fieldID)
jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) 複製代碼
javap -s 命令工具能夠查看一個類的方法的簽名
javap -s xxx.class
類型 | 相應的簽名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int I | |
long | L |
float | F |
double | D |
void | V |
String | Ljava/lang/String |
Array | [Ljava/lang/Object |
Method | (para s1,para s2) 返回值簽名 |
NIEnv提供了一下函函數(可以實現子類對象調用父類方法的功能)
CallNonvirtual
CallVoidMethod(jobject obj, jmethodID methodID, ...)
void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)複製代碼
調用實例方法的三種方法
Call
boolean funcation(int i, double d, char c) {
}
env->CallBooleanMethod( obj, id_function , 100L, 3.44, L'3');複製代碼
Call
Call
```
jvalue是一個聯合體
[cpp] view plain copy
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l; 複製代碼
} jvalue;
jvalue *args = new jvalue[3];
args[0].i = 100L;
args[1].d = 3.44;
args[2].c = L'3';
env->CallBooleanMethodA(obj, id_funcation, args);
delete [] args;
```複製代碼
使用NewObject來建立Java對象。
jobject NewObject(jclass clazz, jmethodID methodID, ...)複製代碼
須要先活的相應構造器的name,方法名設定爲
jmethodID GetMethodID(jclass clazz, const char *name, const char *sig)複製代碼
例子:
jclass class_date = env->FindClass("java/util/Date");
jmethodID method_id = env->GetMethodID(class_date,"<init>","()V");
jobject now = env->NewObject(class_date, method_id);複製代碼
#### Java字符串和C/C++字符串之間的轉換
- a.在java中字符串String對象是Unicode(UTF-16)碼,每一個字符不管中文仍是英文都佔用兩個字節。
- b.能夠經過JNI接口把Java中的字符串轉換成C/C++中的寬字符串,Java也能夠傳一個UTF-8的字符串(char*) 到C/C++中。
- c.反過來C++能夠經過把一個寬字符串,或者一個UTF-8的字符串來建立一個Java端的對象。
#### memset的注意事項
- C庫函數 void *memset(void *str, int c, size_t n) 複製字符c(unsigned char類型)參數str指向的字符串的前n個字符。
- memset是以字節爲單位,初始化內存塊。
當初始化一個字節單位的數組時,能夠用memset把每一個數組單元初始化成任何你想要的值
`複製代碼
char data[10];
memset(data, 1, sizeof(data)); // right
memset(data, 0, sizeof(data));
char str[50];
strcpy(str,"This is string.h library function");
puts(str);
memset(str,'$',7);
puts(str);
```複製代碼
int data[10];
memset(data, 0, sizeof(data)); // right
memset(data, -1, sizeof(data)); // right
memset(data, 1, sizeof(data)); // wrong, data[x] would be 0x0101 instead of 1複製代碼
方法 | 做用
---|---
GetStringChars | 取得UTF-16編碼的寬字符串(char*)
GetStringUTFChars | 取得UTF-8編碼的字符串(char*)複製代碼
注意在不使用到的使用,要注意使用ReleaseStringChars,或者ReleaseStringUTFChars釋放拷貝的內存,或Java對象的引用。
還可使用下面的方法,這個方法能夠增長返回jvm中java對象的可能性,可是仍是有可能返回相應java串的拷貝。
這個方法沒有相應的GetStringUTFCritical,因爲Java字符串使用的是UTF-16,要轉換成UTF-8編碼的串原本就須要一次拷貝動做。
a.基本類型的數組。
b.對象類型(Object[])的數組。
有一個通用於兩種不一樣數組的的函數:獲得數據的長度函數:GetArrayLength
跟處理字符串類似
Get
須要用
Release
void ReleaseIntArrayElements(jintArray array,
jint *elems,
jint mode) {
functions->ReleaseIntArrayElements(this,array,elems,mode);
}複製代碼
0:對Java數組進行更新,並釋放C/C++的數組。
JNI_COMMIT: 對Java數組進行更新但不釋放C/C++數組。
JNI_ABORT:對Java數組不進行更新,並釋放C/C++數組。
類似GetStringUTFCritical的函數:爲了直接傳回指向Java數組的指針而加入的函數。
void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) {
return functions->GetPrimitiveArrayCritical(this,array,isCopy);
}
void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) {
functions->ReleasePrimitiveArrayCritical(this,array,carray,mode);
}複製代碼
Get
void GetIntArrayRegion(jintArray array,
jsize start, jsize len, jint *buf) {
functions->GetIntArrayRegion(this,array,start,len,buf);
}複製代碼
Set
void SetIntArrayRegion(jintArray array, jsize start, jsize len,
const jint *buf) {
functions->SetIntArrayRegion(this,array,start,len,buf);
}複製代碼
建立一個基本類型的Java數組
JNI中沒有把Java對象類型的數組轉換成C++中的jobject[]的函數。而是直接經過Get/SetObjectArrayElement來對Java的Object數組進行操做。
jobject GetObjectArrayElement(jobjectArray array, jsize index) {
return functions->GetObjectArrayElement(this,array,index);
}
void SetObjectArrayElement(jobjectArray array, jsize index,
jobject val) {
functions->SetObjectArrayElement(this,array,index,val);
}複製代碼
使用上述方式不須要釋放資源。
能夠根據數組長度和初始值來建立某個類的數組
jobjectArray NewObjectArray(jsize len, jclass clazz,
jobject init) {
return functions->NewObjectArray(this,len,clazz,init);
}複製代碼
在jvm中建立的對象被傳到本地的C/C++代碼的時候,會產生引用。根據jvm的垃圾回收機制,只要引用存在,就不會觸發該引用指向對象的被回收。
這些引用分爲三種
局部引用Local Reference:是最多見的引用,局部引用只在native函數中有效。局部引用的存在會阻止其指向對象的回收。
全局引用Global Reference:
能夠跨越多個線程
在多個navtive方法中有效、
全局引用存在期間會阻止其引用java對象在jvm的回收。
全局引用的建立不是JNI自動建立的。
全局引用的建立要顯示的調用NewGlobalRef函數,而釋放須要調用ReleaseGlobalRef
弱全局引用:
與全局引用類似,建立和釋放都須要由編程人員來進行。
多個線程,多個native方法中有效。
區別:這個引用不會阻止jvm回收其指向的引用。
NewWeakGlobalRef與ReleaseWeakGlobalRef來建立和釋放引用。
IsSameObject在弱全局引用中有一個特別的功能。
把NULL傳給要判斷的object,來判斷弱全局引用指向的java對象是否被回收。
a.經過方法名+簽名來查詢jfieldID,jmethod開銷是很是大的。
b.緩存方式:
1.在使用的時候緩存。(Caching at the Point of Use)
使用static類型的局部變量來保存已經查詢過的ID,這樣就不會在每次調用的時候查詢,而是隻查詢一次。
2.在java類初始化的時候緩存。(Caching at Class's inititalizer)
這種加載方式在jvm類的加載和從新加載都會從新呼叫該native方法從新計算ID
public class TestNative
{
static {
initNativeIDs();
}
static natvie void initNativeIDs();
int propInt = 0;
}複製代碼
JNIEXPORT void JNICALL Java_TestNative_initNativeIDs(JNIEnv *env, jobject object)
{
......
g_propInt_id = GetFieldID(clazz, "propInt", "I" );
}複製代碼
若是你以爲此文對您有所幫助,歡迎入羣 QQ交流羣 :644196190
微信公衆號:終端研發部