Xamarin.forms: [Bug] [Android] MainPage not changing in OnResume

Created on 5 Sep 2019  路  14Comments  路  Source: xamarin/Xamarin.Forms

Description

On Android changing MainPage in OnResume have no effect. Looks like it was introduced in https://github.com/xamarin/Xamarin.Forms/pull/4707.

OnResume sent to application here
https://github.com/xamarin/Xamarin.Forms/blob/1d47eb0f70144211a495ce2b6e628ed5735b4e0d/Xamarin.Forms.Platform.Android/AppCompat/FormsAppCompatActivity.cs#L442
when the _currentState == AndroidApplicationLifecycleState.OnRestart

and MainPage changed here
https://github.com/xamarin/Xamarin.Forms/blob/1d47eb0f70144211a495ce2b6e628ed5735b4e0d/Xamarin.Forms.Platform.Android/AppCompat/FormsAppCompatActivity.cs#L389

this check https://github.com/xamarin/Xamarin.Forms/blob/1d47eb0f70144211a495ce2b6e628ed5735b4e0d/Xamarin.Forms.Platform.Android/AppCompat/FormsAppCompatActivity.cs#L392 prevents MainPage change handling

Steps to Reproduce

  1. Start repro
  2. Move app to background
  3. resume app

Expected Behavior

Main page changed

Actual Behavior

Main page doesn't change

Basic Information

  • Version with issue: 4.0 and later
  • Last known good version: 3.6.0.709228
  • IDE: vs 2019

Xf.Sandbox.zip

4.0.0 3 regression in-progress Android bug

All 14 comments

I'm the one who added this check, to prevent the application to react to page change when application is in background which was caused issue.
I used _currentState >= AndroidApplicationLifecycleState.OnPause to not have to specify all the state, but the OnRestart is between OnStop and OnDestroy so it is filtered and I think it is not desired here.
I will make some tests to see if re-adding OnRestart reintroduce the problem fixed by #4707 or not and if it the case, make further adjustments.

I made some tests, for me it works like it's supposed to work.

I explain.

If you use the circle or square button, the app go background and the activity is kept in memory, when you relaunch the app, the activity is resumed and call the resume method.
If you use the back arrow button, the app go background and the activity is destroyed, when you relaunch the app, a new activity is recreated and call the resume method.
This worked before because the old activity was not destroyed and triggered a page change with the wrong and sometime disposed renderer.

As it is now the behavior is consistent with Android native lifecycle.

My advice if you want to have this behavior is to use a static flag in your App class, like this :
```cs
public class App : Application
{
private static bool Resume { get; set; }

public App()
{
    if (!Resume)
    {
        MainPage = new Page1();
    }
    else
    {
        OnResume();
    }

    Resume = true;
}

protected override void OnResume()
{
    MainPage = new Page2();
}

}

I'm not talking about closing app with back button and starting it again.

Sometimes you need to change page (require pin code etc.) on app resume (when activity resumed) after app go to background and this case is not working.

Anyway, ignoring MainPage change in active app is wrong.

Are you sure you pass in the OnResume method and the page not change ?
Because in my test the page not change because OnResume is not called (back arrow press).
Place a breakpoint in your Activity OnDestroy, if it is called when your app go background, a new activity will be recreated and the OnResume will not be called.
If the activity is not destroyed in this case I agree that OnResume must be called, but I don't reproduce this case.

I'm not pressing back button, I'm pressing circle/square button. Activity remains same.
Just run attached reproduction and you will see. OnResume method is called, but MainPage is not changing.

I wasn't reproduced it with the Xamarin.Forms Test app but reproduced it with your sample.
And I was able to make a fix for the regression.
Just have to wait for the team to review it.

Any news on this issue? Having the same request / problem.

Same problem here. I have a manager that manage user sessions and they need to be redirected to login page if session has expired. Works if app is in foreground but not in background or when it hits OnResume.

Same problem. This breaks any session management.

Same issue here.

I've also been lurking and monitoring the progress on this for a while now - any workarounds that I can think of haven't been fruitful. Curious if anyone has any creative strategies. Or word of when this will be introduced in the next release.

@joshykautz I think it's doable to make this fix in user code.
I can't provide the detailled code now, but here the approach.

In your android activity code.

  • Add a private boolean IsRestart
  • Override OnRestart method and set IsRestart to true
  • Override OnResume method and if IsRestart set to true, reset the IsRestart to false then call the method SendResume on the application object (the method is public but hidden from the IntelliSense).

I've not tested it, but I think this will do the same thing as my patch.

I completely agree - I bet doing something like this would work just fine

public static bool isForeground = true;

protected override void OnResume(){
     isForeground = true;
}

protected override void OnSleep()
{
     isForeground = false;
     Device.StartTimer(TimeSpan.FromSeconds(1), () => {
          if (isForeground) {
               Application.Current.MainPage = new NavigationPage(new LoginPage())
               return false;
          }
          return true;
     });
}

This would start running when the OnSleep activity/method is called. It would check every second if the app is in the foreground. In fact, this does work - I just tried it. It feels hacky/tacky but i'll take what I can get. Just some rubber ducking to work through this for now. Maybe it will be handled better in the future 馃し鈥嶁檪 Hopefully someone else will be able to benefit from me leaving this here.

@kvpt
I might have messed something up but I gave your fix a try and it doesn't appear to work for me.

Xamarin App:

protected async override void OnResume()
        {
            if (!string.IsNullOrEmpty(IDToken))
            {
                MainPage = new LoginPage(); //Login Page resets IDToken
                await AlertHelper.ShowAlert("Session", "Session Expired.  Please log back in!");
            }
        }

Android Activity:

protected override void OnRestart()
        {
            base.OnRestart();
            IsRestart = true;
        }
        protected override void OnResume()
        {
            base.OnResume();
            if (IsRestart)
            {
                IsRestart = false;
                Xamarin.Forms.Application.Current.SendResume();
            }
        }

The flow seems to be Android:OnRestart -> Xamarin:OnResume -> Android:OnResume() ->Xamarin:OnResume

The Xamarin:OnResume gets called twice which causes my flow not to work. I

On the first OnResume I think the MainPage gets set because I clear the variable IDToken but it doesn't show the change in MainPage... then on the second OnResume it doesn't make it into the loop to try to set the MainPage a second time. However if you do the following it appears to work. In essence don't send it back to Xamarin the second time.

Android Activity:

protected override void OnRestart()
        {
            base.OnRestart();
            IsRestart = true;
        }
        protected override void OnResume()
        {
            base.OnResume();
            if (IsRestart)
            {
                IsRestart = false;
                Xamarin.Forms.Application.Current.MainPage = new LoginPage();
                //Don't Resend back to Xamarin
                //Xamarin.Forms.Application.Current.SendResume();
            }
        }
Was this page helpful?
0 / 5 - 0 ratings