Mvvmcross: Hangs on splashscreen when reopening app with tap on app icon

Created on 27 Jul 2018  路  28Comments  路  Source: MvvmCross/MvvmCross

馃悰 Bug Report

We encounter the issue that the app hangs on the splash screen when reopening the app using the app icon.
It works fine when you reopen the app using the Android Task manager.

Expected behavior

App is in the background and when you tap on the app icon, the app will reopen normally.

Reproduction steps

1) Open the app
2) Go to the background
3) Tap on the app icon
4) Splashscreen will appear and the app won't go further until you push the hardware back button.

So only after you press the hardware back button, the app continues opening and the first page will appear.

I've tested it with MvxSplashScreenActivity and MvxSplashScreenAppCompatActivity and it occurs for both.

Configuration

Version: 6.1.2

Platform:

  • [ ] :iphone: iOS
  • [x] :robot: Android
  • [ ] :checkered_flag: WPF
  • [ ] :earth_americas: UWP
  • [ ] :apple: MacOS
  • [ ] :tv: tvOS
  • [ ] :monkey: Xamarin.Forms
android bug

Most helpful comment

Adding NoHistory = true to my Activity fixed the issue for me, but obviously this would not suit everyone.

All 28 comments

It also shows the splash screen when launch it from the Task Manager?

Nope, it that case you don't see the splash screen. Then the last shown page will be re-opened immediately.

Which Activity flags do you have on your Activities?

Think this will be related to the lock around app start that prevents it from running twice. We need to work out where to call reset to allow app start to run again.
@stefan89 do you see this behaviour when you run the playground sample?

@Cheesebaron
Splash activity has these attributes:

Most of the other activities have these attributes:

  • ScreenOrientation = ScreenOrientation.Portrait
  • WindowSoftInputMode = SoftInput.AdjustResize

@nickrandolph I will test that as soon I have time (probably on Monday) and let you know

I went through the reproduction steps on the Playground.Droid project (using a KitKat and a Marshmallow emulators) and couldn't reproduce this error.

@nickrandolph & @nmilcoff I just tested it using the Playground app (master branch) and it seems only to happen when the debugger is attached.

Tested it on an Android Emulator (API level 25) and a physical device (API level 26) and the behaviour is the same.
Hangs on the splash screen (green screen with text: "Loading....")

FYI I am using the latest version of VS for Mac + latest version of Xamarin + latest Android SDK.

I also tested it on a clean empty Xamarin.Android project (without Mvx) with the debugger attached and then I cannot reproduce it.

@stefan89 when you run the Playground app with the debugger attached - did you have to make any changes to get it to break? @nmilcoff does the Playground app work for you even with debugger attached?

Also running into this issue when opening the app via a push notification when the app is backgrounded.

Opening the app in any other way works fine, including opening the app via a push notification when the app is killed.

Adding NoHistory = true to my Activity fixed the issue for me, but obviously this would not suit everyone.

Easily reproduce in every projects, you should wait a bit after you move the app to background and press a lunch icon.
Add this code to splashActivity after on create , it will solve your issue.

if (Intent.Flags.HasFlag(Android.Content.ActivityFlags.BroughtToFront) || Mvx.IoCProvider.CanResolve<IMvxApplication>() /*Sometimes your app in foreground and user pressing on channel notification that cause same issue*/)
{
        Finish();
        return;
}

But better to check the app state if it first time launching or coming from background

I couldn't reproduce this one with the debugger attached (kitkat emulator). Can any of you guys reproduce this in Playground.Droid?

Did you test on real device ?

Works fine also using a Samsung S8 device, with and without the debugger.

  1. Can any of you guys reproduce this in Playground.Droid?
  2. Did you have by any chance "Don't keep activities" enabled?

Another solution is to change MvxSplashScreenAppCompatActivity.RunAppStartAsync

if (startup.IsStarted)
{
    //When app is reactivated, splash screen activity is displayed over other activities. Remove it.
    if(_isResumed)
        Mvx.IoCProvider.Resolve<IMvxAndroidCurrentTopActivity>().Activity.Finish();
    return;
}

Tp reproduce: when the app is in background, a push notif is received, and tapping it reopens the app. In this case, the SplashActivity is opened over all existing activities. This is both a correct and incorrect behavior. It is correct as the push notification does not specify an activity to open. But it is incorrect as mvvmcross should handle this correctly.

When I would have a time I will try to repro this issue in Playground.Droid.
There are two ways how could I achieve this. Android 9 Nokia 7

  1. Click on app icon while app is in background. This would start again splash screen (MainLauncher = true, NoHistory = true), as a result it would hang on splash screen.
  2. When you click not on push notification message directly , but click on notification channel , as result it will start MainLauncher Activity. You can even click on notification channel while your app in foreground and see same behavior.

@softlion right now Im using this code

            if (Intent.Flags.HasFlag(ActivityFlags.BroughtToFront))
            {
                Finish();
                return;
            }
            else if(Mvx.IoCProvider.CanResolve<IMvxApplication>())
            {
                if(Mvx.IoCProvider.Resolve<IMvxAppStart>().IsStarted)
                {
                    Finish();
                    return;
                }
            }

I can repro on Android 5/23, not when you tap the app icon, but when you tap a notification.

UPDATE:
I don't know if it is related to this issue, but since MvvmCross 6.2.x I am facing issues when reopening an app when it was in the background for more than 15 minutes.
In that case the app hangs on a white screen.

When I downgrade MvvmCross to version 6.1.2 I don't have this issue.

Hi,
We are experiencing this issue.
The app doesn't go past the splash-screen when the app was a long time in the background.
This issue is easy reproducible by killing the app in task manager and reopening it.
When the app hangs on the splash you can pass it by going to Android home and reopen the app.

This is a really annoying issue for our users.
We are experiencing since the the update to 6.2.2

My solution:

1) Transform your SplashActivity into a standard activity.

public class SplashActivity : AppCompatActivity // MvxSplashScreenAppCompatActivity

2) Register the activity lifecycle in android's MainApp:

    public class MainApp : MvxAppCompatApplication<Setup,App>
    {
        private StartupLifecycleCallback activityLifecycle;

        [Android.Runtime.Preserve]
        protected MainApp(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
        }

        [Android.Runtime.Preserve]
        public MainApp()
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();
            activityLifecycle = new StartupLifecycleCallback();
            RegisterActivityLifecycleCallbacks(activityLifecycle);
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                UnregisterActivityLifecycleCallbacks(activityLifecycle);
                activityLifecycle.Dispose();
            }

            base.Dispose(disposing);
        }
}

3) And all the heavy code that was in SplashActivity is now independant of this activity which won't bother you anymore. code of StartupLifecycleCallback:

class StartupLifecycleCallback : Java.Lang.Object, Application.IActivityLifecycleCallbacks, IMvxSetupMonitor
    {
        private int startedActivities;
        private bool isSplashDisplayed;
        private Bundle _bundle;
        private MvxMessage<PushNotificationData> pushedData; //Non null when the app is launched from a push notification
        private MvxAndroidSetupSingleton setupSingleton;
        private Activity splashActivity;

        public void OnActivityCreated(Activity activity, Bundle bundle)
        {
            if (activity is SplashActivity)
            {
                _bundle = bundle;

                //Non null when the app is launched from a push notification
                var extras = activity.Intent?.Extras;
                if (extras != null)
                {
                    var customData = new Dictionary<string, string>();
                    foreach (var key in extras.KeySet())
                    {
                        var o = extras.Get(key);
                        if(o!=null)
                            customData.Add(key, o.ToString());
                    }

                    pushedData = new MvxMessage<PushNotificationData>(this, new PushNotificationData { CustomData = customData });
                }
            }
        }

        public void OnActivityDestroyed(Activity activity)
        {
            if (activity.IsTaskRoot && Mvx.IoCProvider!=null && startedActivities==0)
            {
                //Application deactivated
                setupSingleton?.CancelMonitor(this);
            }
        }

        public void OnActivityPaused(Activity activity)
        {
        }

        public void OnActivityResumed(Activity activity)
        {
            if (activity is SplashActivity theSplashActivity)
            {
                //Splash can be resumed twice, for example when appcenter distribute is embedded and the app is first run (as appcenter temporarily switches to the device's browser before the real main activity is displayed)
                //Also, Splash can be recreated by tapping an Android notification while the app is in background. In this last case, startup.IsStarted will be true (and startedActivities will be greater than 1).
                if (!isSplashDisplayed)
                {
                    isSplashDisplayed = true;
                    splashActivity = activity;
                    setupSingleton = setupSingleton ?? MvxAndroidSetupSingleton.EnsureSingletonAvailable(Application.Context);
                    setupSingleton.InitializeAndMonitor(this); //Resume is too late here, the top activity monitor is not up, and will not detect that the current top activity is a splash screen. The fragment presenter will be unable to present the activity.
                }
            }
            else if (isSplashDisplayed)
            {
                isSplashDisplayed = false;
                splashActivity = null;
                //app is now in Foreground
            }
            else if(startedActivities == 1)
            {
                //app is now in foreground
            }
        }

        public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
        {
        }

        /// <summary>
        /// Android always starts new activity just before stopping previous one. 
        /// </summary>
        /// <param name="activity"></param>
        public void OnActivityStarted(Activity activity)
        {
            startedActivities++;
        }

        public void OnActivityStopped(Activity activity)
        {
            if (startedActivities == 1 && (!(activity is SplashActivity) || isSplashDisplayed))
            {
                if(Mvx.IoCProvider.TryResolve<INetworkInfo>(out var networkInfo))
                    ((NetworkInfo)networkInfo).SetState(ApplicationState.Background);
            }

            if (startedActivities > 0)
                startedActivities--;
        }

        /// <summary>
        /// Setup triggers this 'initialization completed' event
        /// </summary>
        Task IMvxSetupMonitor.InitializationComplete()
        {
            var hint = GetAppStartHint(_bundle);
            _bundle = null;

            //Non null when the app is launched from a push notification
//           if (pushedData != null)
//                ((AppCenterService)Mvx.IoCProvider.Resolve<IMyAppCenterService>()).DeferedMessenger.Publish(pushedData);

            return RunAppStartAsync(hint);
        }

        protected virtual object GetAppStartHint(Bundle bundle)
        {
            return null;
        }

        private Task RunAppStartAsync(object hint)
        {
            var startup = Mvx.IoCProvider.Resolve<IMvxAppStart>();

            if (startup.IsStarted)
            {
                //App has been reactivated from background (via a notification or a tap on the app's icon)

                //When app is reactivated, splash screen activity is displayed.
                //Remove it before triggering Activation state change.
                if(isSplashDisplayed)
                    Mvx.IoCProvider.Resolve<IMvxAndroidCurrentTopActivity>().Activity.Finish();
                ((NetworkInfo)Mvx.IoCProvider.Resolve<INetworkInfo>()).SetState(ApplicationState.Foreground);

                return Task.CompletedTask;
            }

            //App 1st start
          ((Application.IActivityLifecycleCallbacks)Mvx.IoCProvider.Resolve<IMvxAndroidCurrentTopActivity>()).OnActivityResumed(splashActivity);
            return startup.StartAsync(hint);
        }
    }

@softlion are you able to reproduce the issue in the Playground sample? If so, can you submit a PR showing the issue and perhaps even suggest a fix?

I'm experiencing similar issue to you with a real app.
This article I think describes the problem:
https://stackoverflow.com/questions/19545889/app-restarts-rather-than-resumes/23220151#23220151
This describes that the launch icon puts the launch activity (i.e. splash screen) in activity stack when it shouldn't, so that you can end up with an activity stack like this:

  1. Splash
  2. Next
  3. Splash

The simple fix is to put

   if (!IsTaskRoot)
            {
                Finish();
            }

in OnCreate of the splash as shown below:
```
public class SplashActivity : MvxSplashScreenAppCompatActivity
{
public SplashActivity() : base(Resource.Layout.splash_background)
{

    }

    /// <summary>
    /// Finishes if not at root
    /// https://stackoverflow.com/questions/19545889/app-restarts-rather-than-resumes/23220151#23220151
    /// </summary>
    /// <param name="bundle">Bundle.</param>
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        if (!IsTaskRoot)
        {
            Finish();
        }
    }

```

@Cheesebaron I'm thinking that we should permanently in https://github.com/MvvmCross/MvvmCross/blob/develop/MvvmCross.Android.Support/V7.AppCompat/MvxSplashScreenAppCompatActivity.cs OnCreate.

Stack Overflow
Hopefully someone can help me figure out, if not a solution, at least an explanation for a behaviour. The Problem: On some devices, pressing the launcher icon results in the current task being re...
GitHub
The .NET MVVM framework for cross-platform solutions, including Xamarin.iOS, Xamarin.Android, Windows and Mac. - MvvmCross/MvvmCross

@SOFSPEEL
This solves the issue.
I agree that this should be permanently be included in the MvxSplashScreenAppCompatActivity.

Hi, we have a similar issue that described @SOFSPEEL, and his fix works for some cases, but also we have issue with the freezing of the splash screen with next steps:

  1. LaunchApp, SplashActivity
  2. Then in Navigates to SetupViewModel where we pre-setup our app.
  3. Navigate to LoginViewModel with flags intent.AddFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
  4. Press the hardware back button.
  5. Launch the app using app icon, SplashScreen freezes.

MvvmCross 6.2.3
Uses for SplashView:
android:noHistory="true"
and intent-filter:

The hack that we use to prevent it from freezing is to OnBackPressed and MoveTaskToBack(true);

One more case:
We have foreground service running on one of the Activity. We start it OnCreate, and stop OnDestroy methods.
If the user closes task by swiping it from the Task manager, and click on the icon to launch the app, it freezes on the SplashScreen.

Maybe someone faced same issues and know the resolution ? Thank you in advance.

@KurasovAnton a permanent fix is available here. https://github.com/MvvmCross/MvvmCross/pull/3376

Works fine in 2 apps with services, local and remote push notifs since 6 months.
Tested with all the cases you described.

Was this page helpful?
0 / 5 - 0 ratings