JNI 通过多个 JNI 调用使 C++ 中的对象保持活动状态
Posted
技术标签:
【中文标题】JNI 通过多个 JNI 调用使 C++ 中的对象保持活动状态【英文标题】:JNI Keep Object in C++ alive over multiple JNI calls 【发布时间】:2016-05-09 14:07:15 【问题描述】:我的 Java 原生音频库有问题,但首先,这是我目前的方法:
-
使用本机方法,我打开了一个“全局”流,它通过回调函数接收数据。
回调函数一直运行到没有数据为止。
如果没有数据,流只会停止,但不会关闭。
现在我想再次向流提供数据 [尝试再次启动流(允许此操作)],但流已被删除。
所以现在我试图弄清楚如何防止从 C++ 或 Java 中删除流。
一种解决方案是在流中创建一个线程,以防止删除。
但我不喜欢这个解决方案...
所以我搜索了如何使这些对象保持活动状态,并发现可以使用 JNI 进行所谓的“全局引用”。但我不明白它们是否仅适用于 java 对象或两者兼而有之。
我还尝试了另一种 C++ 指针类型是否有帮助。
感谢任何帮助或想法,它不必只是 JNI。 C++ 标准库方法/函数/类等也不错:)!
系统信息:
编译器:MinGW64 over MSYS2 JDK8u91 当然是64位操作系统(不必命名为xD)使用全局流意味着所有 JNI 方法都可以访问该流。
编辑:
好的,“让猫从后面出来”我正在使用 RtAudio。 Realtime C++ Audio Library
例子:
//THIS IS C++ CODE
RtAudio audio(RtAudio::WASAPI);
int callback(//Buffer stuff etc.)
//do something
if(data.isEmpty())return 1;//invokes audio.closeStream() but this does NOT closes the stream!
else return 0; //Go on with the stream rather wait for the next call
JNIEXPORT void JNICALL openStream(jintArray data)
//This is a outputstream
audio.openStream(&outputParams,....., &callback,....);
audio.startStream();
JNIEXPORT void JNICALL fillData(jintArray data)
//filldata again!
stream.start(); //Starts the stream but does nothing, because the stream is deleted because of Java
如果我将 openStream 方法更改为此,流不会被删除,但我会寻找更好的解决方案...
JNIEXPORT void JNICALL openStream(jintArray data)
//This is a outputstream
audio.openStream(&outputParams,....., &callback,....);
audio.startStream();
**while(true); //ADD THIS AND THE STREAM WON'T BE DELETED!**
另一个解决方案是在 RtAudio API 中添加一个“keepInstanceAliveThread”,它在 stopStream() 方法之后调用,并在调用 startStream() 或 closeStream() 之后删除。我宁愿选择其他解决方案,但根本没有。
预结果:
感谢@marcinj:
众所周知,全局对象会导致许多问题,很难控制它们的构造/破坏。
编辑: 我在互联网上(也在 *** 上)发现,析构函数是在 JNI 方法返回后调用的。
【问题讨论】:
你知道如何让全局对象在内存中保持活跃吗?即使打开多个活动,我也试图让一个对象保持活动状态。 对不起,我没有发现任何新东西:( 它们用于 Java 对象和类。您也可以考虑弱全局参考。但是audio
到底是什么?它在哪里声明?它是 Java 对象还是 C++ 对象?
【参考方案1】:
我们可以创建一个新线程来继续在 JNI 函数中运行,其中的变量引用您的对象。
然后单独的调用将停止函数中线程的执行。
////////////////////////////////////
另一种选择是创建对象的全局引用
android JNI and NewGlobalRef。
在这里,单独调用 USB 断开连接即可DeleteGlobalRef
。
或通过将 C++ 对象的生命周期传递回 java 层来将其移动到 java 中 keep some sort of c++ object alive over multiple jni calls。 这里单独调用 USB 断开连接将删除 Java 代码中对 C++ 对象的任何引用。
1) 或 2) 中的任何一个的原因都不是,创建的帧然后从堆栈中退出,并且 JNI 本地引用被删除。
当所有 std::thread 引用(理想情况下未分离)被删除时,这将结束 C++ 中的所有线程。在非分离的情况下,std::thread 析构函数在主出口被调用,或者当线程对象超出范围然后调用 terminate() 时。在分离的情况下,分离的线程在应用程序关闭时退出,这会杀死主机进程。
////////////////////////////////////
【讨论】:
【参考方案2】:在 Java 对象中使用 long
来保存指向 C++ 对象的指针。
Java long
是 64 位的,Java 运行的每个平台都有 32 位或 64 位指针。并且为 Java 提供的每个平台都将支持这一点,尽管它不是严格符合 C 或 C++ 代码。
Java:
// class member
private long audio
// native functions
private native long openStream( int[] data );
private native void deleteStream( long audio );
private native void nativeFillData( long audio, int[] data );
public MyClass()
audio = openStream( data );
public void fillData( int[] data )
nativeFillData( this.audio, data );
// delete the C++ object - you may want to
// control this directly and not rely on
// finalize() getting called
protected void finalize()
deleteStream( audio );
super.finalize();
C++:
JNIEXPORT jlong JNICALL openStream(jintArray data)
RtAudio *audio = new RtAudio(RtAudio::WASAPI);
audio->openStream(&outputParams,....., &callback,....);
audio->startStream();
// C-style cast - JNI interface is C, not C++
return( ( jlong ) audio );
JNIEXPORT void JNICALL deleteStream(jlong jaudio)
RtAudio *audio = static_cast <RtAudio *>( jaudio );
delete audio;
JNIEXPORT void JNICALL nativeFillData(jlong jaudio, jintArray data)
RtAudio *audio = static_cast <RtAudio *>( jaudio );
audio->start();
...
【讨论】:
以上是关于JNI 通过多个 JNI 调用使 C++ 中的对象保持活动状态的主要内容,如果未能解决你的问题,请参考以下文章
Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)