Friday, 21 June 2013

The Android Activity lifecycle

When I first started developing Android apps, I knew there was a lifecycle for activities but I didn't really care because I, like many, assume that all Activities very quickly move from one state to another and nothing runs for long enough to be a problem. This is bad thinking and there are two reasons why this has caught up with me:

  1. If your activity has a slow running process, such as connecting to a web service, your activity might be destroyed while this is happening. If you have referenced things incorrectly, you can cause your activity to leak!
  2. Even if you think like a stack, an activity might be destroyed underneath the current activity for any reasons, mostly related to resources. This means when you exit and return to the calling activity, it be created again (by calling onCreate) before it then calls handlers like onActivityResult. This can screw things up if, like me, you have a habit of putting code into onCreate which actually belongs elsewhere.
  3. Rotating the screen causes the current activity to be destroyed and recreated potentially doing something twice that should only happen once.
To be fair, most activities are probably little more than a set of widgets which are displayed and then things only happen when you press buttons etc. These will automatically be saved and restored into their current state but anything more than that or things that you don't want to be repeated have to use various techniques.

Firstly, you need to consider carefully what would be done in onCreate and what should be done in, perhaps, onStart or onResume. By referencing the Android Activity Lifecycle diagram (there are loads on Google), you should work out what you would need to happen every time, for instance, the activity is paused and resumed, what you would need to happen only after destroying etc. and place the code in the relevant places. For instance, anything to do with layouts and views should be done in onCreate since it will be called each time the orientation changes and the resources might need different images/layouts etc for the new orientation. If, as in my case, I want to force the orientation in a particular activity to be landscape, my first call in onCreate is to check the orientation, and if it is not correct, set the requested orientation and immediately return. This will cause the activity to be destroyed, re-orientated and then onCreate called again, at which point I can load all the resources etc.

Long running processes are another thing that can catch you out. What happens if you start something and then the user turns the phone round? I'll tell you what happens, the activity will be destroyed, recreated and you would possible call your long running process again. Firstly, you should carry out this activity in another thread, using something like a subclass of AsyncTask, which allows your UI to remain responsive and allows you to, for instance, update the screen with progress while you wait. I found a good tutorial here: http://www.techrepublic.com/blog/... and this is cool because it also protects against the potential of a destroyed activity by allowing the activity to detach and re-attach to the thread if it is recreated and not run the process again.

The last issue is a little harder. Imagine this situation. I have an activity which itself calls a long running process to authorise the mobile device with a web service. If this is successful, it starts another activity. When the child finishes and calls finish(), I expect the parent's onActivityResult to be called but for some reason, the parent was destroyed beneath the child and is being recreated so onCreate is called before onActivityResult. This is documented but is a problem. There is no obvious way to distinguish between the activity being called the first time and being called after exiting the child process as far as I know. There are a couple of things that can be useful but these are a little flaky and are only workarounds as far as I know. The first is that the bundle state passed into the onCreate method will always be null the first time into the method so this can indicate that you are coming in and need to call the long running process. I have done this but I am concerned that there might be places where this is not null (like rotating the screen) which should also call the long running process but which won't. The other, more horrible but more reliable, is to set a flag in the application configuration which says whether you are entering or exiting the activity. I supposed, you could store the result of the long running process into configuration and then use this to dictate whether to do it again or not, it depends on what your long running process does.

I would be interested on whether there is a proper way to determine this, such as a piece of data passed in saying whether this is a new-new Activity or whether it was destroyed for lack of resources and is being recreated.
Post a Comment