定义: 至关于一个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
微信公众号:终端研发部