Reputation: 3665
I'm getting the following error triggered by React Native. I'm pretty sure its linked to a custom view I am using.
The view is generated by some C++ code (via OpenGL) which is added to a ViewGroup
and then incorporated into a React Native view. The first time the app loads, everything is fine. However, when some data changes, it seems that React wants to reload the view, and create a new instance of the view. I am checking if the view has a parent and if so, delete it, before returning the view. However, after the user selects new data, just as the views are about to update, the crash occurs.
The ViewManager code is as follows:
public class ReactJuceViewManager extends SimpleViewManager<JuceViewHolder> {
public static final String REACT_CLASS = "ReactJuceView";
private ReactApplicationContext reactContext;
public ReactJuceViewManager(ReactApplicationContext reactContext)
{
super();
this.reactContext = reactContext;
}
@Override
public String getName()
{
return REACT_CLASS;
}
@Override
protected JuceViewHolder createViewInstance(ThemedReactContext themedReactContext) {
JuceBridge juceBridge = JuceBridge.getInstance();
//JuceBridge manages the a HashMap of `JuceViewHolder`s, never deleting them once created
Log.d("ReactJuceViewManager", "createViewInstance");
JuceViewHolder v = juceBridge.getViewForComponent("Tuner", themedReactContext);
Log.d("JuceViewHolder", v.getTag().toString());
ViewGroup parent = (ViewGroup) v.getParent();
if (parent != null)
{
Log.d("ReactJuceViewManager", "JuceViewHolder parent: " + v.getParent().toString());
// Remove from parent
Log.i("ReactJuceViewManager", "Removing JuceViewHolder instance from previous parent");
parent.removeView(v);
}
else
{
Log.d("ReactJuceViewManager", "JuceViewHolder parent: null");
}
return v;
}
}
.. and here is the stack trace:
E/unknown:React: Exception in native call from JS
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4309)
at android.view.ViewGroup.addView(ViewGroup.java:4145)
at android.view.ViewGroup.addView(ViewGroup.java:4086)
at com.facebook.react.views.view.ReactViewManager.addView(ReactViewManager.java:196)
at com.facebook.react.views.view.ReactViewManager.addView(ReactViewManager.java:39)
at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:384)
at com.facebook.react.uimanager.UIViewOperationQueue$ManageChildrenOperation.execute(UIViewOperationQueue.java:175)
at com.facebook.react.uimanager.UIViewOperationQueue$2.run(UIViewOperationQueue.java:782)
at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:829)
at com.facebook.react.uimanager.UIViewOperationQueue.access$1500(UIViewOperationQueue.java:44)
at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:868)
at com.facebook.react.uimanager.GuardedChoreographerFrameCallback.doFrame(GuardedChoreographerFrameCallback.java:32)
at com.facebook.react.uimanager.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:131)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:856)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Upvotes: 0
Views: 3133
Reputation: 1093
I encountered a similar problem and got it working by removing parent of the custom view if the view existed already.
You should probably return a new component on createViewInstance though.
static CustomView customView;
@Override
public CustomView createViewInstance(@NonNull ThemedReactContext reactContext) {
if (customView == null) {
customView = new CustomView(reactContext);
} else {
ViewGroup parent = (ViewGroup) customView.getParent();
if (parent != null) {
parent.removeView(customView);
}
}
return customView;
}
Upvotes: 1
Reputation: 3665
I changed how the JuceBridge
stores references to Components - storing ComponentPeerView
s instead of the View
that is the JuceViewHolder
.
(See my fork of JUCE)
I then accessed the ComponentPeerView
's parent which solved my issue:
protected ReactJuceView createViewInstance(ThemedReactContext themedReactContext) {
JuceBridge juceBridge = JuceBridge.getInstance();
juceBridge.setActivityContext(themedReactContext);
ReactJuceView v = new ReactJuceView(themedReactContext);
JuceBridge.ComponentPeerView componentPeerView = juceBridge.getPeerViewForComponent("MainComponent");
if (componentPeerView != null) {
Log.d ("ReactJuceViewManager", "Got componentPeerView");
ViewGroup parent = (ViewGroup) componentPeerView.getParent();
if (parent != null) {
parent.removeView(componentPeerView);
}
v.addView(componentPeerView);
Log.d( "ReactJuceViewManager", "About to attachOpenGlContext");
MainActivity.attachOpenGLContext("MainComponent");
}
else
Log.d("ReactJuceViewManager", "componentPeerView is null");
return v;
}
Upvotes: 0