1.cpp文件

由于.cpp是jni方法,所以在最前面要加上include ,其实就是用原生的C实现了java与C交互一套原则即JNI规范。

.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之间的基本类型的映射关系:

img

2)JNI 引用类型

引用类型我们并不能直接的使用,它不是以原生数据类型的形式展现,而是需要通过JNI提供的一组相关的API把引用类型提供给原生代码使用。

img

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);

然而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;

分类: JNI

0 条评论

发表回复

您的电子邮箱地址不会被公开。