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

时间:2016-09-24 14:19:57

标签: java android c++ react-native java-native-interface

我有一个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;

}

作为参考,createJuceWindow定义为:

JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, createJuceWindow, jobject, (JNIEnv* env, jclass, jobject view))
{
    JuceView::getInstance().createJuceWindow(view);
}

调用:

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
}

我已将JuceViewHolder派生自ViewGroup,我通过JNI将其传递给C ++函数,以附加从(C ++){{3}生成的(Java)ComponentPeerView构造函数

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

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

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

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

这最初有效,但似乎如果我切换到另一个View或Activity和/或切换回C ++ View,我会遇到类似于以下内容的崩溃:

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)

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

然而,我使用的Component::addToDesktop方法的方法签名使用void*

virtual void addToDesktop (int windowStyleFlags,
                               void* nativeWindowToAttachTo = nullptr);

(这是iOS中用于传递原始UIView以附加Component的方法)

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

0 个答案:

没有答案