maui
maui copied to clipboard
Java.Lang.IllegalStateException after MainPage swap
Description
Exception Java.Lang.IllegalStateException
is thrown when I try to change the Application.Current.MainPage
after the browser opened with WebAuthenticator.Default.AuthenticateAsync
is closed without any login.
Steps to Reproduce
- Checkout the attached repo
- Go to the example OIDC server https://openidconnect.net/ and copy the generated URL
- Paste the URL into
MainPage.xaml.cs
line 16 - Run app
- Click the "Login" button
- Close the browser, do not attempt to login at all
- Exception is thrown on line 25
Link to public reproduction project repository
https://github.com/czmirek/maui_auth_mainpage_fail
Version with bug
7.0 (current)
Last version that worked well
Unknown/Other
Affected platforms
Android
Affected platform versions
Android 12, API 31, x86_64
Did you find any workaround?
Yes, see this commit in another branch of the repo. I figured out that MainActivity.OnResume
must run first before MainPage
is swapped. It looks like a race condition. The workaround is simple with TaskCompletionSource
but I'm not sure if this is going to work in all cases.
Relevant log output
{Java.Lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at Java.Interop.JniEnvironment.InstanceMethods.CallIntMethod(JniObjectReference instance, 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 11725
at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeAbstractInt32Method(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 492
at AndroidX.Fragment.App.BackStackRecord.Commit() in /Users/runner/work/1/s/generated/androidx.fragment.fragment/obj/Release/net6.0-android/generated/src/AndroidX.Fragment.App.BackStackRecord.cs:line 131
at Microsoft.Maui.Platform.NavigationRootManager.SetContentView(IView view) in D:\a\_work\1\s\src\Core\src\Platform\Android\Navigation\NavigationRootManager.cs:line 147
at Microsoft.Maui.Platform.NavigationRootManager.Connect(IView view, IMauiContext mauiContext) in D:\a\_work\1\s\src\Core\src\Platform\Android\Navigation\NavigationRootManager.cs:line 88
at Microsoft.Maui.Handlers.WindowHandler.CreateRootViewFromContent(IWindowHandler handler, IWindow window) in D:\a\_work\1\s\src\Core\src\Handlers\Window\WindowHandler.Android.cs:line 79
at Microsoft.Maui.Handlers.WindowHandler.MapContent(IWindowHandler handler, IWindow window) in D:\a\_work\1\s\src\Core\src\Handlers\Window\WindowHandler.Android.cs:line 24
at Microsoft.Maui.PropertyMapper`2.<>c__DisplayClass5_0[[Microsoft.Maui.IWindow, Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Handlers.IWindowHandler, 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.Window.OnPropertyChanged(String propertyName) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Window\Window.Impl.cs:line 253
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.Window.set_Page(Page value) in D:\a\_work\1\s\src\Controls\src\Core\HandlerImpl\Window\Window.Impl.cs:line 113
at Microsoft.Maui.Controls.Application.set_MainPage(Page value) in D:\a\_work\1\s\src\Controls\src\Core\Application.cs:line 107
at MauiApp1.MainPage.OnCounterClicked(Object sender, EventArgs e) in C:\Users\lesar\source\repos\maui_auth_mainpage_fail\MauiApp1\MainPage.xaml.cs:line 27 --- End of managed Java.Lang.IllegalStateException stack trace ---java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1601) at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1641) at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:341) at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:306) at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.n_onResume(Native Method) at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.onResume(WebAuthenticatorIntermediateActivity.java:41) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1476) at android.app.Activity.performResume(Activity.java:8191) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4747) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4790) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7839) 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:1003) --- End of managed Java.Lang.IllegalStateException stack trace ---java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1601) at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1641) at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:341) at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:306) at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.n_onResume(Native Method) at crc6468b6408a11370c2f.WebAuthenticatorIntermediateActivity.onResume(WebAuthenticatorIntermediateActivity.java:41) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1476) at android.app.Activity.performResume(Activity.java:8191) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4747) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4790) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7839) 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:1003)}
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
For anyone finding this, I found this issue is also related to https://github.com/dotnet/sdk/issues/28880 and for the code to work in release, you need to add <AndroidLinkResources>false</AndroidLinkResources>
to your MAUI csproj.
Missclick closed, sorry :(
I have the same issue, when setting a new mainpage when a timer ticks and the user need to re-authenticate.
Looks very similar to #11259
@rkops-bd can you attach a repro? I'm not able to reproduce this with the original posters sample.
@czmirek I was able to reproduce your issue. From my reproduction, it seems like the issue happens because the MainPage swap is happening while the app is stopped so your workaround is waiting for the app to re-resume. That's something we should fix but I'm just leaving a note here with my observations for other people that come across this.
Verified this on Visual Studio Enterprise 17.7.0 Preview 1.0. Repro on Android 13.0-API33 with provided Project: MauiApp1.zip
Anybody who's having trouble with this, here is a workaround, it's not great, but app stopped crashing:
public class App : Microsoft.Maui.Controls.Application
{
public static bool IsSleeping { get; set; }
protected override void OnStart()
{
IsSleeping = false;
base.OnStart();
}
protected override void OnSleep()
{
IsSleeping = true;
base.OnSleep();
}
protected override void OnResume()
{
IsSleeping = false;
base.OnResume();
}
public void SetMainPage()
{
MainThread.BeginInvokeOnMainThread(async () =>
{
while (IsSleeping)
{
await Task.Delay(500);
}
Current.MainPage = new MainPage();
});
}
}
FYI: https://github.com/dotnet/maui/issues/19304
I think this issue invokes the same codepaths as whatever is causing this to fail. Once this issue gets addressed, we should reassess the above issue to verify if it's fixed too (My guess is that it should, since I think it's the same underlying cause). If it's not, we should reopen it.
I found out that the only workaround to temporarily "fix" it now is setting LaunchMode = LaunchMode.SingleInstance
instead of SingleTop
.
Possible related https://github.com/dotnet/maui/issues/15788
Forwarding a comment PureWeen made in a possibly related issue https://github.com/dotnet/maui/issues/18159#issuecomment-1883645003
I found the cause in Android. https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/FragmentManager.java
Error Message : "Can not perform this action after onSaveInstanceState"
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
Even if you just minimize the app while it is on splash screen it will crash (just setting the main page inside of App's constructor. Same if you set it inside IWindowCreator).
This seems to only happen when a debugger is attached?
This seems to only happen when a debugger is attached?
No. It crashes on release builds too. and on debug without debugger attached too. Just open the app and while on the splashscreen minimize it and then return back (maybe wait a few sec before returning back).
It can be fixed by changing commit()
to commitAllowingStateLoss()
when fragments are switched. And I remember I saw Xamarin does exactly that and it doesn't have such problem. I don't know the consequences of such fix though.