Description
openedon Jun 28, 2023
Description
Maui crashes on Android when you create a View (such as a button), assign it to a Layout, assign that layout to the Content property and then reuse that same control in a 2nd layout. When you assign the 2nd layout to Content property it causes a crash.
The crash results in the error:
Java.Lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
This is not reproducible on iOS but consistently happening on Android (tested on Android 13 but not other versions). Ideally Maui would detach any Views from its parent before adding a View to a different Layout.
Another point is the layout must be assigned to the Content property for this error to happen. For example, if you assign the same control to 2 layouts but only assign 1 layout to Content that is okay. But when you assign the 2nd layout to the Content property it breaks.
Steps to Reproduce
- Create new Maui app. File -> New Solution -> Maui App
- Create a Layout, such as a VerticalStack and assign it to the page Content
- Add a button to the stack and assign the layout to the Content property of the page. Keep a reference to the button.
- Create a second VerticalStack containing the same button
- Assign the second layout to the Content property
Result: App Crashes with error "Java.Lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first."
Expected: To be able to reassign a button to different layouts without having to manually remove it from previous layouts.
Link to public reproduction project repository
https://github.com/gerhartz/maui_android_crash
Version with bug
7.0.86
Last version that worked well
Unknown/Other
Affected platforms
Android
Affected platform versions
Broken on Android 13, possibly earlier versions
Did you find any workaround?
- Manually remove a View from its parent before adding it to another Layout
((Layout)view.Parent)?.Remove(view);
-
Avoid reassigning a control to a different layout after it has already been added to avoid hitting the IllegalStateException.
-
Duplicating the control and adding the duplicate to a layout instead of reusing the original can be a workaround, although in some cases this is not feasible to maintain multiple duplicate controls.
Relevant log output
Java.Lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualVoidMethod(JniObjectReference instance, JniObjectReference type, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 12324
at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 75
at Android.Views.ViewGroup.AddView(View child) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net7.0/android-33/mcw/Android.Views.ViewGroup.cs:line 1999
at Microsoft.Maui.Handlers.LayoutHandler.SetVirtualView(IView view) in D:\a\_work\1\s\src\Core\src\Handlers\Layout\LayoutHandler.Android.cs:line 43
at Microsoft.Maui.Handlers.ViewHandler`2[[Microsoft.Maui.ILayout, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Platform.LayoutViewGroup, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].SetVirtualView(IElement view) in D:\a\_work\1\s\src\Core\src\Handlers\View\ViewHandlerOfT.cs:line 56
at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler newHandler) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Element\Element.Impl.cs:line 69
at Microsoft.Maui.Controls.Element.set_Handler(IElementHandler value) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Element\Element.Impl.cs:line 19
at Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.IElement.set_Handler(IElementHandler value) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\VisualElement\VisualElement.Impl.cs:line 303
at Microsoft.Maui.Platform.ElementExtensions.ToHandler(IElement view, IMauiContext context) in D:\a\_work\1\s\src\Core\src\Platform\ElementExtensions.cs:line 96
at Microsoft.Maui.Platform.ElementExtensions.ToPlatform(IElement view, IMauiContext context) in D:\a\_work\1\s\src\Core\src\Platform\ElementExtensions.cs:line 127
at Microsoft.Maui.Handlers.ContentViewHandler.UpdateContent(IContentViewHandler handler) in D:\a\_work\1\s\src\Core\src\Handlers\ContentView\ContentViewHandler.Android.cs:line 44
at Microsoft.Maui.Handlers.ContentViewHandler.MapContent(IContentViewHandler handler, IContentView page) in D:\a\_work\1\s\src\Core\src\Handlers\ContentView\ContentViewHandler.Android.cs:line 49
at Microsoft.Maui.PropertyMapper`2.<>c__DisplayClass5_0[[Microsoft.Maui.IContentView, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Handlers.IContentViewHandler, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<Add>b__0(IElementHandler h, IElement v) in D:\a\_work\1\s\src\Core\src\PropertyMapper.cs:line 172
at Microsoft.Maui.PropertyMapper.UpdatePropertyCore(String key, IElementHandler viewHandler, IElement virtualView) in D:\a\_work\1\s\src\Core\src\PropertyMapper.cs:line 47
at Microsoft.Maui.PropertyMapper.UpdateProperty(IElementHandler viewHandler, IElement virtualView, String property) in D:\a\_work\1\s\src\Core\src\PropertyMapper.cs:line 72
at Microsoft.Maui.Handlers.ElementHandler.UpdateValue(String property) in D:\a\_work\1\s\src\Core\src\Handlers\Element\ElementHandler.cs:line 87
at Microsoft.Maui.Controls.Element.OnPropertyChanged(String propertyName) in D:\a\_work\1\s\src\Controls\src\Core\Element.cs:line 383
at Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, Boolean silent) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 533
at Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 469
at Microsoft.Maui.Controls.BindableObject.SetValue(BindableProperty property, Object value, Boolean fromStyle, Boolean checkAccess) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 391
at Microsoft.Maui.Controls.BindableObject.SetValue(BindableProperty property, Object value) in D:\a\_work\1\s\src\Controls\src\Core\BindableObject.cs:line 365
at Microsoft.Maui.Controls.ContentPage.set_Content(View value) in D:\a\_work\1\s\src\Controls\src\Core\ContentPage.cs:line 14
at AndroidCrash.MainPage.Add() in /Users/pgerhartz/Documents/GitHub/maui_android_crash/AndroidCrash/MainPage.xaml.cs:line 34
at AndroidCrash.MainPage.<.ctor>b__2_0(Object sender, EventArgs args) in /Users/pgerhartz/Documents/GitHub/maui_android_crash/AndroidCrash/MainPage.xaml.cs:line 19
at Microsoft.Maui.Controls.Button.Microsoft.Maui.Controls.Internals.IButtonElement.PropagateUpClicked() in D:\a\_work\1\s\src\Controls\src\Core\Button.cs:line 278
at Microsoft.Maui.Controls.ButtonElement.ElementClicked(VisualElement visualElement, IButtonElement ButtonElementManager) in D:\a\_work\1\s\src\Controls\src\Core\ButtonElement.cs:line 85
at Microsoft.Maui.Controls.Button.SendClicked() in D:\a\_work\1\s\src\Controls\src\Core\Button.cs:line 253
at Microsoft.Maui.Controls.Button.Microsoft.Maui.IButton.Clicked() in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Button\Button.Impl.cs:line 24
at Microsoft.Maui.Handlers.ButtonHandler.OnClick(IButton button, View v) in D:\a\_work\1\s\src\Core\src\Handlers\Button\ButtonHandler.Android.cs:line 153
at Microsoft.Maui.Handlers.ButtonHandler.ButtonClickListener.OnClick(View v) in D:\a\_work\1\s\src\Core\src\Handlers\Button\ButtonHandler.Android.cs:line 174
at Android.Views.View.IOnClickListenerInvoker.n_OnClick_Landroid_view_View_(IntPtr jnienv, IntPtr native__this, IntPtr native_v) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net7.0/android-33/mcw/Android.Views.View.cs:line 2285
at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V(_JniMarshal_PPL_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:line 121
--- End of managed Java.Lang.IllegalStateException stack trace ---
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:5248)
at android.view.ViewGroup.addView(ViewGroup.java:5077)
at android.view.ViewGroup.addView(ViewGroup.java:5017)
at android.view.ViewGroup.addView(ViewGroup.java:4989)
at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:31)
at android.view.View.performClick(View.java:7506)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1219)
at android.view.View.performClickInternal(View.java:7483)
at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
at android.view.View$PerformClick.run(View.java:29357)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7884)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
--- End of managed Java.Lang.IllegalStateException stack trace ---
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:5248)
at android.view.ViewGroup.addView(ViewGroup.java:5077)
at android.view.ViewGroup.addView(ViewGroup.java:5017)
at android.view.ViewGroup.addView(ViewGroup.java:4989)
at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:31)
at android.view.View.performClick(View.java:7506)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1219)
at android.view.View.performClickInternal(View.java:7483)
at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
at android.view.View$PerformClick.run(View.java:29357)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7884)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)