Android Game Singleton or Application or neither - android

I'm attempting to learn the ropes of Game Development and Android and it's been a great learning experience so far, but the Android Activity Lifecycle stuff has me a little stuck.
Let's say I have three Activities -
Game Menu
Level Select
The Actual Game (2d Game using the SurfaceView).
I would think I need to load all Bitmaps and Sounds outside the Actual Game Activity to prevent from having to unload and reload them everytime onDestroy and onCreate is called, for example, like when the phone goes to sleep, or everytime a new level is chose from the level select screen.
This led me to the Singleton approach which makes sense and seems to be what I'm after, but I don't fully understand where I would need to unload the Singleton to make sure it gets GC'd, for example, could I release it in the Game Menu onDestroy, if so, how do I know if the Game Menu Activity will hang around indefinately. I've also seen mention of Android being able to kill the singleton on its own. I have also read some about using the Application Context that would give me lifecycle.
What would be the best way to approach this? Do I really want to unload and reload all the resources in the Game Activity? Am I going in the right direction with the Singleton or Application idea?

Always assume your activity will be destroyed. Assuming otherwise will always give you headaches.
Personally I've taken the approach of having my Loading and Main Game activity one in the same in the sense that while the surfaceview is being created I can overlay a loading graphic/screen on top of the surfaceview and preload all the bitmaps/sounds in onCreate.
The level select and other activities are then spawned from the main activity, typically through a startActivityForResult. This allows you to 1) Create a loading screen while preloading/instantiation takes place 2) Present a main screen with Start Game etc... 3) Have the surfaceView already created but being hidden or behind the main game activity.
Obviously you shouldn't be starting threads or anything until they start the game but the surfaceview itself can be ready to go. In that way Start Game is seamless and quick. You just hide all the other Views and bring your SurfaceView to the top.
But again, always assume your activity will be destroyed. Meaning saving game state etc...
With regards to the Main Game you should split your game into 2 threads - one for game logic and one for the actual drawing on the canvas. In your Game Activity you should override:
onCreate: Preload bitmaps, sounds, instantiate main game class and start game thread (UI/Canvas in addition to the game logic thread) when the Start Game button is pressed
onDestroy: Kill all threads
onPause: Pause all threads (canvas thread should be killed on surfaceDestroy)
onResume: resume thread
Always profile your app using the tools available. Have your application update threads and track it using the resource tracker to make sure you aren't using temporary objects. Always create such objects in your main activity/class and reference them. My app never runs GC unless I tell it to and it runs smoothly using the aforementioned design.
Hope this helps. I've written some posts with regards to Android Game development at methodin.tumblr.com.

Activity's onDestroy method is not guaranteed to be called (e.g. Task killer), so don't depend on it.
If you need to presist some class across your application cycle, Singleton or reference them in Application Class is also applicable, indeed, Singleton seems makes more sense to cleaner design.
I don't think you need to explictly remove your stuff, since GC will release everything once it is not needed (at least it will once your application is killed by android). Furthermore, attaching those objects to Game Menu's lifecycle is a bad idea. I guess your activities would like:
Game Menu Activity -> Level Selection Activity -> Main Game Activity
It is sometimes possible that, while you are in "Main Game Activity", the "Game Menu" is actually destroyed. (Yeah and if you press 'back', it will switch to a newly created game menu)

The Application class is a place to store information that will be available for the Application lifecycle. This is a good place to put things that are used by multiple activies (global application state). This is essentially the same as using a singleton.
However, if the user leaves the application by pressing home, there is no guarantee that the Application will live on forever. If they come back 20 minutes later the app may need to get recreated.
I don't think you need to worry about things being GCed, Android will take back memory if it needs it. Otherwise, things will be released when the Application exists.
Make sure that you read the information on the Activity lifecycle closely though. If you go from GameActivity -> Level Select -> GameActivity, most likely the GameActivity will not be destroyed and recreated. Instead, it will just be stopped and restarted. This is not a guarantee, but will be true in most cases.

You shouldn't have to worry about GC yourself. When a user leaves your app (and doesn't return for a while), the OS itself will take care of disposing the app for you.
If you're wanting to use a singleton, you can declare it at the Application level. For example an "AssetManager" is available here:
public class YourGameApplication extends Application {
public AssetManager assetManager = new AssetManager();
#Override
public void onCreate()
{
super.onCreate();
}
}
You can then call it from another activity:
YourGameApplication application = ((YourGameApplication ) this.getApplication());
application.assetManager.someFunction();

Related

Keep some variables initialized on destroy()

I have an app that requires fairly substantial initialization (CPU-wise, not memory). When the user hits the back button, I'd prefer to leave the hard computations and allocated structures intact so there is no 3-4 second delay every time they hit the icon.
What is the proper way to do this? Should part of the app be a service?
Decouple your UI and core logic. Since you want your application keep running in the background implement a service component and maintain the state of your native library initialisation and other stuff which you wanted to be available for the UI. Service plus a state machine approach will give you better benefit in such cases.

Subclassing application, Launching nested activities, and detect when last one has exit

I'd like someone to help me with this question regarding an application lifecycle.
My App starts an activity StoryActivity, that can launch other instances of itself, so when the user presses the BACK button, returns to the previous StoryActivity.
I maintain a cross-activity cache in a static hashmap that I'd like to flush on the activity's OnDestroy, but only if this is the last of my spawned activities, i mean, only if the user has destroyed all the stacked (linked) stories and this is the only one left. Kind of an application shutdown.
Do I need to subclass Application, or what's the best practice for my intentions?
I'd like to avoid tracking myself the activity nesting if possible, relying on the android standard lifecycle as much as possible.
Thanks in advance.

How to make Android app recover from pause and resume gracefully

I am very familiar with the Android activity lifecycle, but I feel like I am missing something that should be pretty obvious here.
I have a multi-activity Android app; it uses static variables (in the application class) to keep a handle on the various objects that are used across the different views. The problem occurs when the app is paused (exited) and Android (presumably) cleans up memory to maintain foreground processes. When the user resumes the app, it occasionally (not always) seems to come back and resume in the Activity that the user left, but with the static variables nulled. Even worse, the activity sometimes seems to resume with the static variables still present, but with internal variables in objects nulled.
I eventually implemented a simple sanity check, which checks that the statics are not nulled (and also the most vital inner variables of relevant objects) and returns the app to start if it fails. This did cut down on a lot on the problems, but I still see the occasional issues with this, as it is simply not practical to check everything for every resume().
What I really need is for the app to restart from scratch if the Android OS decides it needs to clean anything non-GC from memory while the app is inactive. I feel there should be a graceful way to do this, but haven't noticed anything in the documentation.
Any thoughts? How do others avoid this problem?
Using the Application class for preserving state can result in unexpected behaviour if Android decides to kill your process completely. Check out this answer to a similar question
i.e. you should probably use some sort of persistence (SharedPreferences or some DB) in your Activity lifecycle callbacks.

Android Activity Garbage Collection

I noticed this behavior while developing a simple Android game which has 2 activities.
The game has 2 activities, the first is a screen which allows the user to select the opponent type, level etc. and the second the actual game screen.
The second activity creates an object of a GameManager class which handles all the game processing.
This GameManager class also creates a CountDownTimer which it starts to prompt user input (on timeout the game is defaulted to the opponent).
I've noticed that if the user exits the second activity (returns to the first) and then launches a new game again, the previous timer is still running until completion.
I've handled this by explicitly cancelling the timer (from the onDestroy() of the second activity) as just setting the timerobject to 'null' did not cancel the timer.
However I'm curious as to why the previous timer was running even after my activity was exited the first time? Shouldn't the GC have deleted all the objects instantiated by the second Activity (and whatever child objects it created) when it was exited? Would be great to know the reason behind the observed behavior?
TIA
Shouldn't the GC have deleted all the objects instantiated by the second Activity (and whatever child objects it created) when it was exited?
This isn't how Garbage Collection works. The GC isn't responsible for 'deleting objects' - it's responsible for picking up 'orphaned' objects and freeing their resources. Even then, a GC isn't guaranteed to get to all of the orphans in a timely manner.
Further to that, any objects which may be 'system' objects and need to be released explicitly may never be released if your code doesn't do it. Other issues with GC may include creating objects which other threads (other than the Activity which created them) may have a reference to.
You mention your 'timer' but don't explain what sort of class you are using for it. I suggest read up specifically about that class and see what the implications are for ceation/deletion (possibly explicit 'release' of resources).
GC is a very grey area on any platform. With Android it's normally pretty immediate but with the nature of the Activity life-cycle it's very difficult to predict what will happen.
In general make use of onCreate, onPause and onResume within Activities and also things like savedInstanceState and SharedPreferences to keep track of what is going on.
CountDownTimer is not bound to an activity as you already found out. A hint to lookout for in these cases is that a class does not receive any Context in its constructor. Hence it can't be bound to an activity.

Handle screen orientation changes when there are AsyncTasks running

I've been bugged by this for a while. How do I properly handle screen orientation changes while I have a separate Thread / AsyncTask running? Currently, I have
android:configChanges="orientation|keyboard|keyboardHidden"
in my AndroidManifest.xml, but that is not really encouraged:
Note: Using this attribute should be avoided and used only as a last-resort. Please read Handling Runtime Changes for more information about how to properly handle a restart due to a configuration change.
Also, in the 2.3 emulator, it works when switching to landscape, but switching back to portrait fails.
Now, the reason why I use configChanges is because when the user switches orientation, I might have an AsyncTask running, doing some network traffic, and I don't want it stopped.
Is there any other way of doing this, or is there a way of fixing 2.3 to switch back to portrait?
I know about onRetainNonConfigurationInstance, but I'm not sure it would be a good idea to "save" the AsyncTask instance, mainly because the class that extends AsyncTask is not static (so it is tied to the Activity) -- and it needs to be, because in onPostExecute() it calls methods from the Activity instance.
I had a similar problem to your and worked around it by implementing the AsyncTask as part of a class which inherits from Application class. An Application class is available all the life time of the application So you don't have to worry about your AsyncTask getting interrupted unless the whole application will be killed.
To get notified when the task has finished the Activity has to implement a interface which it uses to register itself to the Application class.
When your application is destroyed because of the screen rotation you can unregister your Activity from the Application class and re-register it when it is recreated. If the task finishes between destruction and recreation the result of the operation can be stored in the Application class meanwhile so the Activity can check whether the task is still running or whether the result is already available when it is recreated.
Another advantage is that you have direct access to the applications context because the Application class is a sub class of the Context class.
Take a look the droid-fu library BetterAsyncTask. It is meant to handle this exact case.
http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/
I already popped up similar question here.
Basically there is an example of how to pause/resume an AsynTask on device rotation. However it still does not fit for all cases (sometimes it is not possible to safely suspend the action, such as a new user creation on a remote server). For those "unsafe" cases you need to code somewhat I'd call a tricky "framework". You will see CommonsWare gives github links to the one.

Categories

Resources