NDK之旅—一些常用的基本知识
2017-09-20 22:13
330 查看
参考地址: http://blog.csdn.net/time_hunter/article/details/8740758
作用: 通过JNIEnv的指针能够对Java端的代码进行操作:
a.创建Java对象.
jstring str = (*env).NewStringUTF(“终端研发部”);
jclass jclazz = (*env).GetObjectClass(obj);
b.调用Java对象的方法。
“`
jclz = (*env)->FindClass(env, “java/lang/String”);
jdouble doub = (*env).GetStaticDoubleField()
jintArray arrayResult = NULL;
jclass jclazz = (*env).GetObjectClass(obj);
jint * elements =(*env).GetIntArrayElements(array,NULL);
“`
Get/Set[Static]Method
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig)
jmethodID GetMethodID(jclass clazz, const char *name, const char *sig)
注意sig用来处理函数重载引起的不确定。
然后通过ID获取
javap -s xxx.class
- CallMethod,
- CallStaticMethod,
- CallNonvirtualMethod。
调用实例方法的三种方法
CallMethod(jobject obj, jmethodID id, …);
CallMethodV(jobject obj, jmethodID id,va_list lst);
CallMethodA(jobject obj, jmethodID id, jvalue* v);
使用NewObject来创建Java对象。
需要先活的相应构造器的name,方法名设定为,另外返回值的签名是Void
例子:
b.方式-AllocObject
b.可以通过JNI接口把Java中的字符串转换成C/C++中的宽字符串,Java也可以传一个UTF-8的字符串(char*) 到C/C++中。
c.反过来C++可以通过把一个宽字符串,或者一个UTF-8的字符串来创建一个Java端的对象。
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);
“`
在初始化其他基础类型时,则需要注意,比如
注意在不使用到的使用,要注意使用ReleaseStringChars,或者ReleaseStringUTFChars释放拷贝的内存,或Java对象的引用。
还可以使用下面的方法,这个方法可以增加返回jvm中java对象的可能性,但是还是有可能返回相应java串的拷贝。
这个方法没有相应的GetStringUTFCritical,由于Java字符串使用的是UTF-16,要转换成UTF-8编码的串本来就需要一次拷贝动作。
b.对象类型(Object[])的数组。
有一个通用于两种不同数组的的函数:得到数据的长度函数:GetArrayLength
GetArrayElements,可以把Java基本类型的数组转换成C/C++中的数组,可以拷贝一份传本地代码,也可以把指向Java中的指针直接传回本地代码。
需要用
ReleaseArrayElements
mode的类型:
0:对Java数组进行更新,并释放C/C++的数组。
JNI_COMMIT: 对Java数组进行更新但不释放C/C++数组。
JNI_ABORT:对Java数组不进行更新,并释放C/C++数组。
相似GetStringUTFCritical的函数:为了直接传回指向Java数组的指针而加入的函数。
GetArrayRegion 相似于GetStringRegion的函数:没有Release方法,因为我们是拷贝
SetArrayRegion 可以把指定范围内的Java数组元素用C++中的元素赋值。
创建一个基本类型的Java数组
Array NewArray(jsize size),指定长度后返回相应Java基本类型的数组。
使用上述方式不需要释放资源。
可以根据数组长度和初始值来创建某个类的数组
这些引用分为三种
局部引用Local Reference:是最常见的引用,局部引用只在native函数中有效。局部引用的存在会阻止其指向对象的回收。
全局引用Global Reference:
可以跨越多个线程
在多个navtive方法中有效、
全局引用存在期间会阻止其引用java对象在jvm的回收。
全局引用的创建不是JNI自动创建的。
全局引用的创建要显示的调用NewGlobalRef函数,而释放需要调用ReleaseGlobalRef
弱全局引用:
与全局引用相似,创建和释放都需要由编程人员来进行。
多个线程,多个native方法中有效。
区别:这个引用不会阻止jvm回收其指向的引用。
NewWeakGlobalRef与ReleaseWeakGlobalRef来创建和释放引用。
把NULL传给要判断的object,来判断弱全局引用指向的java对象是否被回收。
b.缓存方式:
1.在使用的时候缓存。(Caching at the Point of Use)
使用static类型的局部变量来保存已经查询过的ID,这样就不会在每次调用的时候查询,而是只查询一次。
2.在java类初始化的时候缓存。(Caching at Class’s inititalizer)
这种加载方式在jvm类的加载和重新加载都会重新呼叫该native方法重新计算ID
如果对ndk不了的同学们可以一下参考博客:
NDK探究之旅:
NDK和JNI初探及其联系和区别
NDK探究之旅《一》——对jni和NDK的认识
NDK探究之旅《二》——C语言的基本认识
NDK探究之旅《三》—C语言的输入输出函数
NDK探究之旅《四》——指针的强化理解
NDK探究之旅《五》——指针和数组之间的关系
NDK探究之旅《六》—函数的指针、结构体、枚举、宏定义
NDK探究之旅《七》—函数指针,宏定义的优缺点及应用场景
NDK探究之旅《八》——jni的开发流程规范及环境配置
NDK探究之旅《九》——jni开发中常见的错误及其注意事项
NDK探究之旅《十》——ndk项目实战之Androidstudio开发经验总结
NDK探究之旅《十一》—C代码调用Java代码之项目实战
NDK项目实战—高仿360手机助手之卸载监听s
关于更多:
2017上半年技术文章集合—184篇文章分类汇总
(欢迎关注学习和交流)
JNIEnv
定义: 相当于一个jni上下文对象。作用: 通过JNIEnv的指针能够对Java端的代码进行操作:
a.创建Java对象.
jstring str = (*env).NewStringUTF(“终端研发部”);
jclass jclazz = (*env).GetObjectClass(obj);
b.调用Java对象的方法。
“`
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]Method
JNI中通常用JType指代Java环境中的类。
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;
JType都继承自JObject
[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 {};
jobject的理解
JNIEXPORT void JNICALL Java_com_jue_testnative_TestNative1_hello(JNIEnv *, jobject); 这里是jobject指代的在Java中调用native方法的java类实例
获取jclass的方法
a. jclass FindClass(const char *name) b. jclass GetObjectClass(jobject obj)
FindClass注意事项:
*注意*: 会在ClassPath下面寻找类。需要传入完整类名,注意包与包之间用’/’。jclass class_str = env->FindClass("java/lang/String");
jfiledID/jmethodID的获取
在natvie方法中获取/设置字段的值,或者方法调用,需要先获取相应的field/method的IDjfieldID GetFieldID(jclass clazz, const char *name, const char *sig)
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
Sin签名含义细节
类型 | 相应的签名 |
---|---|
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) 返回值签名 |
jni如何调用Java里面的方法的
NIEnv提供了一下函函数(能够实现子类对象调用父类方法的功能)- CallMethod,
- CallStaticMethod,
- CallNonvirtualMethod。
``` CallVoidMethod(jobject obj, jmethodID methodID, ...) void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args) void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args) ```
调用实例方法的三种方法
CallMethod(jobject obj, jmethodID id, …);
boolean funcation(int i, double d, char c) { } env->CallBooleanMethod( obj, id_function , 100L, 3.44, L'3');
CallMethodV(jobject obj, jmethodID id,va_list lst);
CallMethodA(jobject obj, jmethodID id, jvalue* v);
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;
Java对象的创建
方式-NewObject使用NewObject来创建Java对象。
jobject NewObject(jclass clazz, jmethodID methodID, ...)
需要先活的相应构造器的name,方法名设定为,另外返回值的签名是Void
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);
b.方式-AllocObject
jclass cls = (*env).GetObjectClass(obj); jobject oobj = (*env).AllocObject(cls);
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
获取字符串
a.取得与某个jstring对象相关的Java字符串方法 | 作用 |
---|---|
GetStringChars | 取得UTF-16编码的宽字符串(char*) |
GetStringUTFChars | 取得UTF-8编码的字符串(char*) |
还可以使用下面的方法,这个方法可以增加返回jvm中java对象的可能性,但是还是有可能返回相应java串的拷贝。
这个方法没有相应的GetStringUTFCritical,由于Java字符串使用的是UTF-16,要转换成UTF-8编码的串本来就需要一次拷贝动作。
数组分为2种
a.基本类型的数组。b.对象类型(Object[])的数组。
有一个通用于两种不同数组的的函数:得到数据的长度函数:GetArrayLength
处理基本类型数组
跟处理字符串相似GetArrayElements,可以把Java基本类型的数组转换成C/C++中的数组,可以拷贝一份传本地代码,也可以把指向Java中的指针直接传回本地代码。
需要用
ReleaseArrayElements
void ReleaseIntArrayElements(jintArray array, jint *elems, jint mode) { functions->ReleaseIntArrayElements(this,array,elems,mode); }
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); }
GetArrayRegion 相似于GetStringRegion的函数:没有Release方法,因为我们是拷贝
void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf) { functions->GetIntArrayRegion(this,array,start,len,buf); }
SetArrayRegion 可以把指定范围内的Java数组元素用C++中的元素赋值。
void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint *buf) { functions->SetIntArrayRegion(this,array,start,len,buf); }
创建一个基本类型的Java数组
Array NewArray(jsize size),指定长度后返回相应Java基本类型的数组。
处理Object类型数组
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); }
java对象在JNI中的引用
在jvm中创建的对象被传到本地的C/C++代码的时候,会产生引用。根据jvm的垃圾回收机制,只要引用存在,就不会触发该引用指向对象的被回收。这些引用分为三种
局部引用Local Reference:是最常见的引用,局部引用只在native函数中有效。局部引用的存在会阻止其指向对象的回收。
全局引用Global Reference:
可以跨越多个线程
在多个navtive方法中有效、
全局引用存在期间会阻止其引用java对象在jvm的回收。
全局引用的创建不是JNI自动创建的。
全局引用的创建要显示的调用NewGlobalRef函数,而释放需要调用ReleaseGlobalRef
弱全局引用:
与全局引用相似,创建和释放都需要由编程人员来进行。
多个线程,多个native方法中有效。
区别:这个引用不会阻止jvm回收其指向的引用。
NewWeakGlobalRef与ReleaseWeakGlobalRef来创建和释放引用。
IsSameObject
IsSameObject在弱全局引用中有一个特别的功能。把NULL传给要判断的object,来判断弱全局引用指向的java对象是否被回收。
缓存jfieldID,jmethodID.
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" ); }
如果对ndk不了的同学们可以一下参考博客:
NDK探究之旅:
NDK和JNI初探及其联系和区别
NDK探究之旅《一》——对jni和NDK的认识
NDK探究之旅《二》——C语言的基本认识
NDK探究之旅《三》—C语言的输入输出函数
NDK探究之旅《四》——指针的强化理解
NDK探究之旅《五》——指针和数组之间的关系
NDK探究之旅《六》—函数的指针、结构体、枚举、宏定义
NDK探究之旅《七》—函数指针,宏定义的优缺点及应用场景
NDK探究之旅《八》——jni的开发流程规范及环境配置
NDK探究之旅《九》——jni开发中常见的错误及其注意事项
NDK探究之旅《十》——ndk项目实战之Androidstudio开发经验总结
NDK探究之旅《十一》—C代码调用Java代码之项目实战
NDK项目实战—高仿360手机助手之卸载监听s
关于更多:
2017上半年技术文章集合—184篇文章分类汇总
相信自己,没有做不到的,只有想不到的
如果你觉得此文对您有所帮助, 欢迎关注微信公众号:终端研发部 这里不仅仅学到的是技术(欢迎关注学习和交流)
相关文章推荐
- asp常用的一些基本知识
- Linux一些基本常用命令
- Java学习笔记----一些常用却不清楚的知识
- 信息论中的一些基本知识
- 简要说明python的一些基本知识(二)
- 信息论中的一些基本知识
- Tensorflow一些常用基本概念与函数(一)
- C#之栈的一些基本知识
- shell学习。一些基本的知识。
- 关于android和java环境和编译的一些基本知识
- Tensorflow一些常用基本概念与函数(1)
- framebuffer 一些基本知识
- Linux一些常用的基本命令
- 电子基础知识:常用电子元器件和基本工具
- asp.net的一些基本知识
- 常用数据结构之树的基本知识
- C++学习(1)--基础知识(1)--一些基本的格式
- 对数据库一些基本知识的认识
- 一些网络的基本知识
- lvm基本知识与常用命令