dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22k stars 1.72k forks source link

Crash upon resuming the app 'window was already activated' #21948

Closed ESO-ST closed 2 months ago

ESO-ST commented 5 months ago

Description

when we minimize the app and then restore it, we get a 'window was already activated' crash.

System.InvalidOperationException: 'Window was already activated'
   at Microsoft.Maui.Controls.Window.Microsoft.Maui.IWindow.Activated()
   at Microsoft.Maui.LifecycleEvents.AppHostBuilderExtensions.<>c.<OnConfigureLifeCycle>b__2_3(UIApplication app)
   at Microsoft.Maui.MauiUIApplicationDelegate.<>c__DisplayClass14_0.<OnActivated>b__0(OnActivated del)
   at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[OnActivated](IServiceProvider services, Action`1 action)
   at Microsoft.Maui.MauiUIApplicationDelegate.OnActivated(UIApplication application)
   at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 58
   at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 94
   at MyProject.Platforms.iOS.Program.Main(String[] args) in C:\siren\SES\MyProject\Platforms\iOS\Program.cs:line 12

This takes place when we are showing a popup page, and then close this popup page (with a slight delay) and immediately minimize the app then restore it. Please see the repro project and follow the repro steps.

Steps to Reproduce

  1. open a popup page (press the "Open Popup Page 1" button)
  2. close the popup page and minimized the app (press the "Close popup page after a 2 seconds delay" button and immediately minimize the app)
  3. restore the app

Link to public reproduction project repository

https://github.com/ESO-ST/WebViewCrashMidLoad/tree/WindowWasAlreadyActivatedCrash https://github.com/dotnet/maui/tree/fix_21948

Version with bug

8.0.20 SR4

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

No response

Did you find any workaround?

no

Relevant log output

No response

XamlTest commented 4 months ago

Verified on VS 17.10.0 Preview 4.0(8.0.20). Can repro this issue on iOS 17.2. WebViewCrashMidLoad.zip

PureWeen commented 4 months ago

I've added the repro to our sandbox project here

https://github.com/dotnet/maui/tree/fix_21948

The bug here is that our code that searches for the keywindow doesn't work because mopups is using it's own UIWindow

image

So when our life cycle code tries to search for the keywindow it doesn't return anything.

Some possible thoughts on fixes

1) in a single window scenario (no SceneDelegate) just locate the one UIWindow 2) instead of tying lifecycle events to the delegate events can we inherit from UIWindow and tie activate/deactive to events inside UIWindow instead so that it's a little closer to the source?

we should probably do one regardless if two is a better solution for this scenario

PureWeen commented 4 months ago

MopupsCrash.zip

Workaround

viswanath-pamarthi commented 4 months ago

@PureWeen Observed the same issue on iOS only with mopups, tried to use your work around. This is not working when UIWindow from binding library is the key window, as IWindow.Activated is not triggered when base.OnActivated(application) is called in AppDelegate(after minimize and try to bring app to foreground). As the window created by maui is not activated in previous scenario, we will see an exception in OnResignActivation that the window is already deactivated.

viswanath-pamarthi commented 4 months ago

@PureWeen

I have added below in OnActivated, this is helping as a work around for scenario mentioned in previous comment:

public override void OnActivated(UIApplication application)
{
            base.OnActivated(application);

            if (Application.Windows.FirstOrDefault() is App.WindowFix windowFix && !windowFix.IsActivated)
            {                
                (windowFix as IWindow).Activated();                
            }
}