目录
1.cpp文件
由于.cpp是jni方法,所以在最前面要加上include
.cpp本质就是c++文件,只是实现了java native方法,按照一定的命名规则,使native方法可以关联到.cpp里的方法。
.cpp文件的除了include代码,其它代码都要写在
extern "C" {
}
模块里.
2.cpp与c文件在写JNI时的一些差异
1)调用同一个方法MethodXX
在.C里应写
(*env)->MethodXX(env,jRootKey, 0);
在.cpp里,就要写成
env->MethodXX(jRootKey, 0);
如果在cpp里调用(*env)->
则会报错:error: base operand of '->' has non-pointer type 'JNIEnv {aka _JNIEnv}'
3.JNI数据类型
参考资料:https://www.cnblogs.com/guanmanman/archive/2017/05/05/6811264.html
原生数据类型同Java一样,都包含两类,基本数据类型与引用类型。
1)JNI 基础数据类型
在JNI当中,基本数据类型可以直接与C/C++的相对应的基本数据类型映射,所以我们可以直接拿来使用并不需要转换。
我们先来看看java、JNI、C之间的基本类型的映射关系:
2)JNI 引用类型
引用类型我们并不能直接的使用,它不是以原生数据类型的形式展现,而是需要通过JNI提供的一组相关的API把引用类型提供给原生代码使用。
jstring转char*
const char* cStr;
cStr = (*env)->GetStringUTFChars(env, jUserName,0); //通过jni的API将jni引用类型转换成原生数据类型(C数据类型)
...
if (cStr) {
env->ReleaseStringUTFChars(jUserName, cStr);
}
上面的代码中,GetStringUTFChars就是JNI所提供的API,通过该方法可以把java中所传递的Stirng字符串转化为原生代码所能识别的字符串,然后在进行下面的比较操作。strcmp方法是C语言中所提供的的比较字符串的方法。
字符串
①:创建字符串
可以在原生代码中使用NewString函数构建Unicode编码风格的字符串实例,假如你使用的是utf-8的编码格式,可以使用NewStringUTF函数来构建,如:
jstring jString = (*env)->NewString(env,"I am from C");
jstring jString = (*env)->NewStringUTF(env,"I am from C");
②:把Java字符串转换成C字符串
Java字符串String是属于引用类型的,它不能够直接的被原生代码使用,为了在原生代码中使用Java传递过来的String串,我们需要借用JNI API,根据编码的不同,分别可以是用GetStringChars和GetStringUTFChars函数进行转换,如我们上面的实例:
const char* cStr = (*env)->GetStringUTFChars(env, jUserName,&isCopy);
isCopy参数是可选的,它是jboolean类型的,它的调用者明确返回的C字符串地址是指向副本黑市指向堆中的对象。
字符串用完记得释放一下
(*env)->ReleaseStringUTFChars(env, javaString, str);
③:获取字符串的长度
int dataLen = env->GetStringUTFLength(originalStr);
这里的originalStr是jstring类型
数组
一般数组相关的操作方法都要成对使用,申请和释放。
①:创建数组
JNI通过New<?>Array函数在原生代码中创建数组,<?>类型可以是byte,short,int等类型,如:
jintArray intArray = (*env)->NewIntArray(env,5);
NewIntArray数组中应该给出明确的数组长度,例如,5就是intArray的数组长度。
②:对数组的操作
如以上介绍,数组属于引用类型,通过Java传递过来的数组我们是无法直接进行操作的,但是根据JNI提供的API我们可以很顺利的进行数组的操作.
1)获取数组长度
jsize size = env->GetArrayLength(arr);
2)获取数组元素
将jbyteArray转换成jint*,jint是jni的基本数据类型,C可以直接引用。
jboolean copy = true;
jint* p = env->GetIntArrayElements( arr, ©);
然而copy这个参数不管是不是false,在JNI层对java传递过来的数组进行修改,在java里打印修改后的结果并没有变。https://cloud.tencent.com/developer/article/1338252
事实上,上面博客作者和我都没有对这个方法作出深刻的研究。我误打误撞地调用ReleaseIntArrayElements方法,发现java里打印的数组竟然改变了。https://blog.csdn.net/xlh1191860939/article/details/77200559这篇文章作者对数组操作的这两个方法作了深刻的讲解。
然而,我通过ReleaseIntArrayElements释放了缓冲区,为啥指针还能访问到数组?
env->ReleaseIntArrayElements(arr,p,0);
LOGD("arr[0] xxx = %d",*p);
3)java数组复制成C数组
通过Get<?>ArrayRegion函数将给定的java数组复制成C数组
示例1:从java传递一个数组到C,在C层对数组元素加1,再返给java。
java代码:
int[] arr = new int[]{1,2,3};
sendArray2JNI(arr);
for (int i = 0; i < arr.length; i++) {
Log.d("CZLog", "newArr[" + i + "]: " + arr[i]);
}
C代码:
extern "C" JNIEXPORT void JNICALL Java_com_example_jni01_MainActivity_sendArray2JNI
(JNIEnv *env, jobject obj, jintArray arr){
jint p2[3] ;
env->GetIntArrayRegion(arr,0,3,p2);
p2[0]++;
p2[1]++;
p2[2]++;
env->SetIntArrayRegion(arr,0,3,p2);
}
疑问1:GetIntArrayRegion的最后一个参数是jint,为啥我声明jint传入GetIntArrayRegion方法报错:
A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x24 in tid 24200 (m.example.jni01), pid 24200 (m.example.jni01)
4)JNI 的 jbyteArray 转换为 c的 unsigned char *
jint Java_cn_hongdameng_aacencoder_AacEncoder_AacWrite(JNIEnv* env,jobject this,jbyteArray jBuffer, jint jBufferSize)
{
jbyte* bBuffer = (*env)->GetByteArrayElements(env,jBuffer,0);
unsigned char* buf=(unsigned char*)bBuffer;
}
5)C的char*转jni的jbyteArray
jbyteArray byteArray = env->NewByteArray(len_enc);
env->SetByteArrayRegion(byteArray, 0, len_enc, (jbyte *) buff_enc);
return byteArray;
0 条评论