通过JNI将Java对象传递给C++,然后通过void*传递回Java

Ada*_*ski 6 c++ java java-native-interface android react-native

我有一个Android应用程序,它同时使用React Native和JNI.C++(通过JUCE库的一个分支)用于生成其中一个视图.

React Native要求从重写的方法返回一个新的视图实例createViewInstance(context).每次更新包含此视图的React Native组件时,似乎都会调用此方法.

这是我的(简化)实现:

protected JuceViewHolder createViewInstance(ThemedReactContext themedReactContext) {
    JuceBridge juceBridge = JuceBridge.getInstance();
    juceViewHolder = new JuceViewHolder(themedReactContext);

    // JNI method: this will trigger JuceBridge.createNewView
    MainActivity.createJuceWindow(juceViewHolder); 

    return juceViewHolder;
Run Code Online (Sandbox Code Playgroud)

}

供参考,createJuceWindow定义为:

JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, createJuceWindow, jobject, (JNIEnv* env, jclass, jobject view))
{
    JuceView::getInstance().createJuceWindow(view);
}
Run Code Online (Sandbox Code Playgroud)

哪个叫:

void createJuceWindow(jobject view)
{
    viewToAttachTo = GlobalRef(view); // [GlobalRef][1] manages NewGlobalRef
    myComponent = std::unique_ptr<MyComponent> (new MyComponent);
    myComponent->setVisible (true);
    myComponent->setOpaque(true);

    if (viewToAttachTo.get() == nullptr)
        DBG ("createJuceWindow: viewToAttachTo null!!!");

    myComponent->setBounds(Desktop::getInstance().getDisplays().getMainDisplay().userArea);
    myComponent->addToDesktop (0, viewToAttachTo.get()); // This passes in the `jobject` held by GlobalRef
}
Run Code Online (Sandbox Code Playgroud)

我已将JuceViewHolder派生与ViewGroup 分开,并通过JNI将其传递给C++函数,以附加ComponentPeerView从(C++)AndroidComponentPeer构造函数生成的(Java)

AndroidComponentPeer (Component& comp, const int windowStyleFlags, void* viewToAttachTo)
    : ComponentPeer (comp, windowStyleFlags),
      usingAndroidGraphics (false),
      fullScreen (false),
      sizeAllocated (0),
      scale ((float) Desktop::getInstance().getDisplays().getMainDisplay().scale)
{
    // NB: must not put this in the initialiser list, as it invokes a callback,
    // which will fail if the peer is only half-constructed.

    if (viewToAttachTo == nullptr) DBG ("viewToAttachTo null");
    view = GlobalRef (android.bridge.callObjectMethod (JuceBridge.createNewView,
                                                      (jboolean) component.isOpaque(),
                                                      (jlong) this,
                                                      (jobject) viewToAttachTo));

    if (view.get() == nullptr)
        DBG ("view null!");
    else
        DBG ("got view.");

    if (isFocused())
        handleFocusGain();
}
Run Code Online (Sandbox Code Playgroud)

通过createNewView方法:(简化在这里)

public final ComponentPeerView createNewView (boolean opaque, long host, ViewGroup viewToAttachTo)
{
    ComponentPeerView v = new ComponentPeerView (viewToAttachTo.getContext(), opaque, host);
    viewToAttachTo.addView(v);
    return v;
}
Run Code Online (Sandbox Code Playgroud)

host是指向C++ AndroidComponentPeer实例的指针.

这最初工作,但似乎如果我切换到另一个视图或活动和/或切换回C++视图,我得到类似于以下崩溃:

JNI ERROR (app bug): accessed deleted global reference 0x100566
art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: use of deleted global reference 0x100566
art/runtime/java_vm_ext.cc:410]     from void com.juce.JuceBridge$ComponentPeerView.focusChanged(long, boolean)
Run Code Online (Sandbox Code Playgroud)

我目前的假设是错误是由于void*指针在某些时候因为垃圾收集器移动底层对象而变得过时,如本文所述.我从文章中得到的是jobject应该用来代替指针.

但是Component::addToDesktop我使用的方法的方法签名使用void*:

virtual void addToDesktop (int windowStyleFlags,
                               void* nativeWindowToAttachTo = nullptr);
Run Code Online (Sandbox Code Playgroud)

(这是iOS中用于传递本机UIView以附加Component到的方法)

我的假设有效吗?如果是这样,是否可以安全地转换void*指向Java对象的指针,将其存储为jobject(通过NewGlobalRef)然后将其传递回Java?