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++ 中的对象保持活动状态的主要内容,如果未能解决你的问题,请参考以下文章

Java程序通过JNI调用C++程序的方法

Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)

如何通过JNI将java中的对象的地址赋值给c++指针变量

转JNI 对象处理

如何使 JNI RegisterNatives callack Java 函数具有 C++ 实例范围?

解析Java的JNI编程中的对象引用与内存泄漏问题