Problem with resuming after back button from Android device

Hi All,

This is my first post here. I’ve been working in C# for many years, but I am relatively new to MonoGame, and am even newer to Android development, with no prior XNA background.

I recently started playing with a simple game and tried deploying it to Android. It works just fine, but I noticed that if I use the BACK button to exit the game, it remains as a background task. This is fine, but when I go to resume the game, instead of the game relaunching, my phone doesn’t seem to respond. If I use the HOME button instead and resume, it works fine, going back to where the game left off.

I tried the same thing with just a skeleton project with just the light blue screen, and had the same results.

When stepping through the code, I do get an OnStart() call, but then get an OnStop() call, which allows for a brief flash, with the same end result, the game closes and remains a background task. The only way I can get it to run properly is to close/terminate it and start over.

When researching this I found several articles that sounded similar, but they did not solve my problem.

I’m using VS2015 Community, MonoGame 3.6, and I’m using a LG G3 with Marshmallow 6.0 to test it.

So here are my questions:

  1. When you click the BACK button to close your Android game, what is the preferred way to handle restoring/restarting your game. Again, the HOME button works fine for restoring it where it was.

  2. Is there Xamarin or Android specific stuff that needs to be done - added resources, system calls, etc., or should this behavior be self-contained in MonoGame’s API wrappers.

  3. I stepped though the code to see what was happening and saw that in MonoGame.Framework.Android -> Game.cs -> Exit() sets a private field “_shouldExit” to true, but I did not see a way to reset this value. If I go in and manually set this to false after the fact, the simple games seems to resume properly.

so…

a) Should should there be a public way to reset this field along with “_suppressDraw,” or
b) Should the fields be protected so a derived class COULD override them, or
c) Is there a “proper” way I just don’t know yet for how this should be handled.

I would think this is common, so I’m probably just in learning mode, but I would appreciate any suggestions or help.

Thank you,

Derek

I have the same problem as well, when performing Exit() after back button pressed and then resuming from the recent tasks the game wont restart.
Any help from someone who ported their game to android?

Hi,

I still haven’t come across the official answer and I would still like to know what is the best practice if anyone knows.

I suspect the preferred approach is to set up Xamarin to handle the back button event outside of MonoGame and to force the application to be killed so that it is not in the background and always starts fresh.

However, I would prefer a MonoGame solution, even it if it just a wrapper around a Xamarin fix or some other template tweak.

Just to see if I could come up with a viable work-around, albeit kludgy, I tried the following. It does work, but it requires that you have a “reset()” method of your own that does the restarting part. It also requires keeping track of the “run” state so it knows when it is in normal run mode vs a exit state.

So here is the work-around I have so far: Take it with a grain of salt, but maybe it will help, until a better approach is given.

I downloaded the MonoGame source code, built the .sln files after downloading get and protobuild (I would have rather had the .sln files), and then opened the MonoGame.Framework.Android solution.

I then made a small change to “Game.cs” in the MonoGame.Framework folder.

After the existing Exit method:

public void Exit()
{
	_shouldExit = true;
	_suppressDraw = true;
}

I added a new method:

public void ClearExit()
{
	_shouldExit = false;
	_suppressDraw = false;
}

I then buit the MonoGame.Framework.Android project and copied the resuting MonoGame.Framework.dll to my game’s project.

I then updated my game.

I added a new enumerable type

public enum GameRunState
{
    Stopped,
    Running,
    Exiting
}

In the Game1 file created by the template, I added the following:

private GameRunState GameRunState { get; set; }

public Game1()
{
	GameRunState = GameRunState.Running;
	// ... your other code ...
}	

protected override void OnActivated(object sender, EventArgs args)
{
	// gets called when game is reactivated, including after returning from back button
	if (GameRunState == GameRunState.Exiting)
	{
		GameRunState = GameRunState.Running;
		ClearExit(); // use the workaround so that it doesn't re-exit
		Reset();     // reset the game to initial start mode (game specific implementation)
	}

	base.OnActivated(sender, args);
}

protected override void Update(GameTime gameTime)
{
	if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
	{
		GameRunState = GameRunState.Exiting;
		Exit();
	}

	base.Update(gameTime);
}

Alternately, if we made the “_shouldExit” property publicly accessible, then we could check it instead of our run state flag.

I hope this helps some, or at least spurs a better solution.

Thanks and good luck.

It’s not ideal, but I used the following code to exit after pressing the back button:
Process.KillProcess(Process.MyPid());

Example here: https://github.com/FlukeFan/MonogameAndroidLag/blob/master/lag/Game1.cs#L76-L80

Hi FlukeFan,

Thank you for your response! It would be nice to not have to kill the process, but this seems like a good overall solution. 1) most importantly, it looks like it will do the job!, 2) it is a quick one liner. 3) it doesn’t require modifying the underlying code with a workaround.

I will give this a try. Hopefully this will help others as well. Thanks again!

Derek

Hi,
I’m not sure about killing the process either. I solved this issue by calling this line:
Game.Activity.MoveTaskToBack(true);

instead of Game.Exit(). Which is btw what MG does too.
I hope its a temporary issue and a fix is coming.

Cheers,
Martin

3 Likes

Hi Martin,

That fix looks much cleaner.

I was about to raise a bug, but it looks like the problem is already raised here: https://github.com/MonoGame/MonoGame/issues/5702

Cheers,
Richard

Thank you Martin! Your idea works quite well. I still need to track if I’m restoring from a BACK operation, in which case I need to reset my game, but that is no different than what I was already doing, plus I don’t need to use a modified code base since I am not calling the Exit method.

SO… we have two good solutions…

  1. from FlukeFan…
    Process.KillProcess(Process.MyPid());

  2. from mpeg88…
    Game.Activity.MoveTaskToBack(true);

Solution 1 has the merit of being a single line solution. It causes the game to reload from scratch and works very much as expected from a user perspective.

The only downside is the program is killed instead of just sitting in the background until called again or timed out (which is how Android wants things to work).

Solution 2 has the merit of letting the game sit in the background which allows for a normal resume. Overall, this is better, but it requires a bit more work - 1) you need to use this call instead of the Exit call. 2) you need to track how you exited (i.e. BACK button vs HOME), so you can reset your game to the beginning (which you must implement) - otherwise, it works like the home button.

Both of these seem reasonable solutions to me. I will most likely use solution 2 myself.

I will say that when they work on updating MonoGame 3.6, they should see about a better built-in solution (i.e. it should just work). The Exit call needs to have a way of resetting the internal variables that it uses to be able to resume (i.e. _shouldExit)

THANKS AGAIN to FlukeFan and mpeg88 for your help!! I’m sure this will help others as well!

Hii,

this is the problem in monogame 3.6 version, monogame 3.5.1 does not have this problem and resumes
perfectly on using Game.Exit().I have checked my game using both versions.

So you can use 3.5.1 with Visual Studio 2015 but 3.5.1 did not work with 2017.

Hi All, I stepped away from MonoGame for a while - life has a way of getting in the way, but have recently come back.

I noticed that with MonoGame 3.8.1 when resuming my Android game, after clicking back (triangle), I would often get the game popping up, then closing back.

I found that the app re-activates (OnActivate was called), and then on the Update, it thinks the Back button was clicked even though it wasn’t. It seems that the GamePad.Back button gets set to true, but only gets set back to false if the OnKeyUp event is called (with the back button referenced). So I supposed that it doesn’t always capture that OnKeyUp. I have a kludgy workaround. I’m hoping someone has a better workaround, or maybe a future release will fix it.

Here is what I have, hopefully it will help someone, or I can use follow up comments as a learning experience.

In the Android Project’s Game1.cs file, I modified the Update method to call the OnKeyUp method to reset this value (side effect of the call). That way when you come back (re-activate) your app, it doesn’t think the back button is still pressed, and then re-exit. I couldn’t reset the Back value directly or call OnKeyUp directly, so I had to do a kludgy solution.

private MonoGameAndroidGameView _monoGameAndroidGameView = null;
private KeyEvent _dummyKeyEvent = null;

protected override void Update(GameTime gameTime)
{
	if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
	{
		try
		{
			if (_monoGameAndroidGameView == null)
			{
				// NOTE: GamePad.Back is STATIC, so we can get away with separate instance referencing it
				_monoGameAndroidGameView = new MonoGameAndroidGameView(Game.Activity, (AndroidGameWindow)Window, this);
			}

			if (_dummyKeyEvent == null)
			{
				// create dummy keyevent so we can call OnKeyUp
				_dummyKeyEvent = new KeyEvent(0, "", 0, KeyEventFlags.Canceled);
			}

			if (_monoGameAndroidGameView != null)
			{
				try
				{
					// Reset our BACK button flag so we can resume on activation, without re-BACKing out...
					// NOTE: SIDE EFFECT of call is setting GamePad.Back = false - so it is NOT stuck on true, which causes exit loop on reactivate
					_monoGameAndroidGameView.OnKeyUp(Keycode.Back, _dummyKeyEvent); 
				}
				catch
				{
					// ignore error (if any) - side effect from above call will still take effect
				}

				Game.Activity.MoveTaskToBack(true);
				base.Update(gameTime);
			}
			else
			{
				throw new Exception("Unable to clear BACK button"); // below catch will do hard exit to prevent loop
			}
		}
		catch
		{
			// unable to close the way we want - just kill the process
			Game.Activity.MoveTaskToBack(true);
			Process.KillProcess(Process.MyPid());
		}
	}
	else
	{
		base.Update(gameTime);
	}
}