疑问点
哪种传值的方式会好一点,涉及到的引用是否有内存问题
简介
使用NDK一般都需要java和c/c++的相互传值,java的值传递给c或者c的值传递给java,java传递给c比较简单,就直接放在参数里就行;c传递给java一般的方式有三种。
- 通过反射调用java的方法传值
- 通过c/c++ return
- 通过参数传递过去,然后让c/c++修改内存
实践
我们来具体看一下这三种方式的使用方式:
1.通过反射调用java的方法传值
jclass clazz = env->GetObjectClass(jobject);
onPrepareId = env->GetMethodID(clazz, "onPrepare", "()V");
//调用方法传递值
env->CallVoidMethod(instance, onPrepareId);
这是反射传值,比较简单,也容易释放内存,在调用完方法后就可以释放到相关内存。但是这种在多个方法嵌套使用的时候就不太友好了,比如我还需要在onPrepare中再调用c/c++的代码,然后返回值需要接收,又要一个在java层写个方法去接手返回值,这样回调方法会有好几个比较乱和难管理。
2.通过c/c++ return
比如我们项目中现在需要把byte[]数组经过native音频重采样然后把值返回给java
Java_xxxxx_PcmResample_resample(JNIEnv *env, jclass clazz, jbyteArray pcm) {
//xxxx省略处理pcm的过程
//buff 是处理的指针数据
jbyte* buff;
jbyteArray data = env->NewByteArray(in.size);
env->SetByteArrayRegion(data, 0, in.size, buff);
return data;
}
那这里拿到值也没问题,但是JNI中NewByteArray 涉及到的内存,就没法回收了,这样会造成内存问题吗?我在android studio做了测试,监测了profiler的memory 状态,这样return传值是不会造成native的内存一直上涨。后来我又把return 去掉直接 测试NewByteArray会不会造成内存问题,发现也不会。这个时候我就猜测NewByteArray并不会造成内存泄漏,跟是否返回没有关系。后来找到了这篇文章,关于JVM的局部引用和全局引用的问题。
https://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/recommend.html
NewByteArray是属于局部变量,走完这个c++的函数,就会被JVM自动释放掉,所以不会有内存问题。但是局部变量需不需要手动删除呢?不删除有没有影响?这个我将在下一篇中跟大家一起探讨。
3.通过修改java传过去的参数
上面的代码改成这个样子
Java_xxxxx_PcmResample_resample(JNIEnv *env, jclass clazz, jbyteArray pcm,jbyteArray result) {
//buff 是处理的指针数据
jbyte* buff;
//这样直接赋值给result 这个参数 就不用new也不用返回值了,java直接拿到result用就可以。这里可以实现吗?
env->SetByteArrayRegion(result, 0, in.size, buff);
}
实践得出 在java取出 byte[] result 是可以拿到他的值的。可以看出这种传值的方式既没有内存问题,又不会存在第一种问题的嵌套问题。就所以这种方式应该是最好的传值方式。当然对于一些错误处理的回调函数,还是用第一种方式比较好,单独的一个方法处理错误返回。
总结
c++往java传值有三种方法
1.反射传值主要用在错误回调里面,比较集中的场景
2.直接return的方式在某些情况下不建议使用,详细可以见下一篇文章
3.第三种修改java传过来的参数应该是最好的方案
水平有限,如有错误,请谅解,也请不吝赐教。
网友评论