maui icon indicating copy to clipboard operation
maui copied to clipboard

Application.Current.Windows clears

Open RobbiewOnline opened this issue 1 year ago • 7 comments

Description

With my application on Android the Application.Current.Windows is populated with one Window object.

After some time (seems to be after requesting permissions on Android) the app is resumed and the Application.Current.Windows results in an empty list.

I haven't tried testing on iOS at this time.

Steps to Reproduce

At this time I can't re-create outside of my app, but I do not think it's app specific.

If I keep a local reference to the Window object then the app continues to let me interact, but Application.Current.Windows results in a list of zero length.

I've created a minimal project which hasn't re-created the issue (yet), but hopefully it'll be a good starting place.

Link to public reproduction project repository

https://github.com/RobbiewOnline/MauiAppWindowTest

Version with bug

9.0.0-rc.2.24503.2

Is this a regression from previous behavior?

No, this is something new

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android

Did you find any workaround?

Keeping a reference to the Window object instead of using Application.Current.Windows

Relevant log output

No response

RobbiewOnline avatar Oct 15 '24 09:10 RobbiewOnline

I'd add a breakpoint to figure out what clears the list...

StephaneDelcroix avatar Oct 16 '24 09:10 StephaneDelcroix

The Windows array is cleared outside of any of the app flows, so no breakpoint will help as it's outside of any code I can influence it.

Besides, if it was to end up with a zero length array in the sample project then the list will re-render when no elements are present, which it hasn't done, yet.

RobbiewOnline avatar Oct 20 '24 17:10 RobbiewOnline

This feels like your Activity is being recreated

It's hard to know why the windows are "disappearing" for your scenario

maybe you're creating a new app that's different than the one being used?

You can try overriding RemoveLogicalChild on the App.xaml.cs to see when it's called

PureWeen avatar Oct 21 '24 17:10 PureWeen

I did notice the MauiProgram was being instantiated more than once, evident by some logging that should only happen once per launch.

It seemed that Sentry.io was causing this unexpected behaviour as it was trying to resolve MauiProgram somewhere and decided to construct itself a new one!

Whether that explains the disappearing Windows elements I'm not sure.

Unfortunately my project still uses Ninject and hasn't moved over to the MAUI IoC system, which I'm tempted to do soon, as maybe this would have prevented this flow.

As a quick workaround I keep a reference to MauiProgram and if CreateMauiApp is invoked more than once then I return back the current MauiProgram instance.

Perhaps in debug mode on mobile the maui runtime could log a warning or assert than the Window array isn't empty - at least that'll catch it early?

I'll override RemoveLogicalChild with logging regardless and see how that goes...

Follow-up, it doesn't seem like App.xaml.cs has such a RemoveLogicalChild method to override? This is based on Intellisense not offering completion and it fails to compile, i.e.

        protected override void RemoveLogicalChild(object child)
        {
            Logger.Log(this, $"RemoveLogicalChild called with child: {child?.GetType().Name ?? "null"}");
            
            base.RemoveLogicalChild(child);
            
            Logger.Log(this, $"RemoveLogicalChild completed for child: {child?.GetType().Name ?? "null"}");
        }

Thank you.

RobbiewOnline avatar Oct 22 '24 08:10 RobbiewOnline

oops I was thinking that was virtual

try protected virtual void OnChildAdded(Element child)

PureWeen avatar Oct 22 '24 15:10 PureWeen

I'm beginning to think my other issue #25443 is related.

It's possible that if CreateWindow is being invoked multiple times by the Android app (i.e. when a persistent notification is resuming the app) then it's likely that the Window array is being cleared as part of that process too?

RobbiewOnline avatar Oct 24 '24 11:10 RobbiewOnline

I did notice the MauiProgram was being instantiated more than once, evident by some logging that should only happen once per launch.

This behavior is somewhat confusing to me.

MauiProgram is tied to the application life cycle

CreateMauiApp is called with the app process is created which only happens once.

If that's being called again that means your entire app is being destroyed and removed from memory and then being started back up again.

Or you have some code that's incorrectly triggering MauiProgram

PureWeen avatar Oct 24 '24 15:10 PureWeen

I'm not very comfortable with the Android Activity lifecycle, but I did set it to use a 'SingleTask'

    [Activity(
        // Label = "MyTeamSafe"
        // Icon = "@mipmap/icon"
        //Theme = "@style/MainTheme",
        Theme = "@style/Maui.SplashTheme",

        MainLauncher = true,
        Exported = true,
        LaunchMode = LaunchMode.SingleTask, // Required for Media playback
        ScreenOrientation = ScreenOrientation.Portrait,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
    public class MainActivity : MauiAppCompatActivity
    {

It shouldn't launch more than once, however there's a persistent foreground notification that's created (because it's a GPS app that captures location updates in a long-running way) and when/if the user clicks on that notification then it launches the main activity, but if it's already running it appears to reuse the same running activity.

RobbiewOnline avatar Oct 29 '24 14:10 RobbiewOnline

What if you turn on that android dev feature to always dispose activities? That would force a popup - like a camera or picker activity to close your app's activity and thus actually really closing and removing the window?

mattleibow avatar Nov 19 '24 05:11 mattleibow

@RobbiewOnline I have LaunchMode.SingleInstance and I still get the crash on MAUI 9.0.21 (this is from a custom navigation service but all it does is just going to Application.Windows to get the window). Also I use IWindowCreator to create a window if that's important. I don't use CreateWindow on Application.

Happening after opening of In app purchase popup and returning back from it judging from the stacktrace (crashes are on Prod).

All 3 crashes happened on Android 14 (too small of a sample to judge that it is only Android 14). I don't have Android 14 but I tested some scenarios and couldn't reproduce it on Android 13. Also 2 of 3 cases where when app is in background.

System.ArgumentOutOfRangeException: ArgumentOutOfRange_IndexMustBeLess Arg_ParamName_Name, index
  ?, in Window List<Window>.get_Item(int)
  ?, in Page NavigationService.GetCurrentPage()
  ?, in async Task NavigationService.NavigateAsync(string key, NavigationParameters parameters)
  ?, in async Task DialogsService.ShowMessage(string title, string message, string buttonTitle, bool closeWhenClickedOutside)
  ?, in async Task UpgradePageViewModel.ExecuteBuyCommand()
  ?, in async void LockAsyncCommand.Execute(object parameter) x 2
  ?, in void Task.ThrowAsync(Exception, SynchronizationContext)+(object state) => { }
  ?, in void SyncContext.Post(SendOrPostCallback, object)+() => { }
  ?, in void RunnableImplementor.Run()
  ?, in void IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
  ?, in void JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)

The only two places that can remove Window from the list are:

https://github.com/dotnet/maui/blob/0f9dfdd2cd39a0313c3fc76848a0de8182599477/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs#L54

https://github.com/dotnet/maui/blob/0f9dfdd2cd39a0313c3fc76848a0de8182599477/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs#L59

VNGames avatar Jan 26 '25 14:01 VNGames

Thanks for adding your details.

Happening after opening of In app purchase popup and returning back from it judging from the stacktrace (crashes are on Prod).

This sounds familiar, as I was losing my reference to Window after trying to set permissions, which can cause the app to background and subsequently resume.

Your IAP flow would be similar.

I don't have Android 14 but I tested some scenarios and couldn't reproduce it on Android 13.

Feel free to add me to your beta with steps to recreate on Android 14.

Sounds like you've narrowed it down...

The only two places that can remove Window from the list are:

Though from what I'd heard the window array was there anyway but CurrentPage hid the implementation, so only in theory I'd expect it to behave exactly the same, if true.

However based on your crashes and mine something seems to have broken.

Thanks again for taking the time to add your findings 🤩

RobbiewOnline avatar Jan 26 '25 14:01 RobbiewOnline

@RobbiewOnline GetCurrentPage is mine, it isn't MAUI's. The first thing it does is getting a window _application.Windows[0] where _application is a reference to Application. Here it fails.

I will be adding a fallback by storing initially created window and using it if the list is empty. In case fallback is used I will log it to breadcrumbs just in case it will crash later to see that it is after fallback, and I think it will crash because I have been getting randomly "Window was already created" or "Window was already destroyed" crashes in the past. If .Destroying() (the method that clears the window list) is called once and later again, the second time it will crash with "Window was already destroyed". Will also add logging of lifecycle android events too. Will release this today to see if I catch something.

VNGames avatar Jan 26 '25 15:01 VNGames

Please see repro: https://github.com/Syed-RI/Crash-with-CreateWindow including video.

  • Pull and run the project
  • Press the back navigation button. Resume the app from the task manager

Syed-RI avatar Jun 25 '25 16:06 Syed-RI