Android activities – The predominance of the UI Thread

/

by

Reading Time: 5 Minuten

Android application user interfaces are decomposed into ‘screens’ called activities. The user navigates from screen to screen thanks to buttons, clickable elements, keyboard keys, gestures, etc. Each activity manages one ‘screen’. Beyond those screens, ‘activity instances’ are created, configured, destroyed, recreated following a somewhat sophisticated lifecycle. To develop an activity, developers need to create a class extending the Activity base class and override a couple of methods to react to lifecycle transition, events etc. This blog post highlights the threading issues encountered when developing a responsive Activity.

The Activity concept is one of the corner stones of Android. An activity is a single, focused ‘thing’ that the user can do. Almost all activities interact with the user, so the Activity takes care to create a ‘screen’ and to manage user requests.

To create an Activity, you need to extend the Activity class. There are two methods almost all subclasses of Activity need to implement namely,

  • onCreate(Bundle) which acts as a ‘constructor’. Inside this method you (re-) initialize the activity (for example, set the content View with the setContentView method, set members…).
  • onPause which is called when the user leaves the activity (the activity is no more visible). An interesting aspect of this method is its ‘killable’ aspect. After this method, the system can decide to kill the activity instance without calling any other methods. So, before exiting this method everything needs to be ‘saved’.

However theses two methods are only the tip of the iceberg. The activity lifecycle is fairly sophisticated.

The activity’s adventure playground

Activities are managed inside a stack composing a ‘task‘ . When a new activity is started, it is placed on the top of the stack and becomes the running and visible activity. The previous activities remain in the stack but are hidden until it becomes the top again (because all other activities started after it are closed).

Basically, activities have four states:

  • Visible and focused: the activity is visible (i.e. on the top of the task), the user can interact with the activity.
  • Visible but without the focus: the activity is still visible, but the user cannot interact with the activity (because of a view that has the focus on the top of the activity (like a dialog)). In that case, the activity is paused, but maintains the state (i.e. member values). It may be killed by the system in extreme low memory situation
  • Hidden: the activity is completely hidden by another activity (full-screen). The activity is stopped. It still retains all state and members BUT is often killed by the system when memory is needed elsewhere.
  • Finished: an activity that is paused or stopped can be killed by the system. The system can either call the onDestroy method or simply kill the process (without any notification). The state is lost in that case. If the activity is displayed again to the user, it must be completely restarted and restore its previous state itself (if at all).
Activity Lifecycle

The previous diagram illustrates the complete activity lifecycle. Method with red outlines are ‘killable‘, i.e. the system may kill the process with no notification after one of these methods. This makes the onSaveInstanceState / onPause methods pretty important to store everything persistently. The onCreate method restores the state.

To persist the state, you can either use the Bundle object injected in the onSaveInstanceState method or use files, preferences, and content providers… In the case of a Bundle object, this Bundle is re-injected in the onCreate method.

Lifecycle callbacks, Services and UI Thread

Despite this fairly complex lifecycle, the activity based development model is very flexible. To react to lifecycle transitions, activities override the adequate methods and change the UI and the state accordingly. Something to notice about activities, however, is the predominance of the UI thread (also called main thread). All callbacks (in fact all methods starting with ‘on‘) are called in the UI Thread and so, can modify the current view from those methods without any issues. However, doing long task inside the UI thread decreases the responsiveness of the application as well as the user experience.

A good example of this kind of problems is when an application tries to use a progress bar or a progress dialog. Updating the progress is made in the UI thread. So, to obtain a smooth animation, nothing else must be done in the UI thread.

It starts to be more tricky when interacting with an Android services (running in the background and managing long running task). If you call startService, the onCreate method from the service runs also in the UI Thread, regardless of the thread you use to call the startService method. The same behavior applies for bindService. The onBind method (called on the Service) runs in the UI Thread regardless of the thread you use to call the bindService method. Moreover, the onServiceConnected method (invoked when the service is bound to retrieve the service object) is also called in the UI thread.

This forces you to reduce the time spent in the onCreate and onBind method of a service, to delegate the complexity to methods called by the service consumer in another thread. So, if you plan to do long operations with your service, you must create new threads and be careful to call such operations outside the UI thread… It can become a nightmare if the created threads are not finished when the activity instance is destroyed (either by the system, or after a reconfiguration like e.g., on orientation changes).

Conclusion

The morale of this story is simple. Remember that all the on* method of your activity run inside the UI Thread. So they can create dialogs, toast, etc. It is also the case if you bind to a service. But if you want to do anything long, you must create another thread. This can become really complex if you have:

  • A lot of threads
  • The activity dying before the threads is done
  • or you need to react correctly to user actions (how to stop work to do something else)

The next posts will explain how to deal with background tasks.


Comments

12 responses to “Android activities – The predominance of the UI Thread”

  1. Hello, after some testings I assume that onSaveInstanceState() method would be called after onPause(), not before. Please fix the image, because someone will be confused.

  2. I can’t believe that services running in the same thread than the ui.it is incredible.

  3. gayathri

    give real time example for reuse components in android

  4. Very nice article

  5. akquinet tech@spree

    @srinivas:
    The onPause and onREsume method are called by the Android directly.

  6. srinivas

    Hi,

    I would like to know who is responsible for calling onPause() and onResume(). Using buttons means every app should have these buttons right?

  7. Hi
    Thanx for good explanation about the stack levels of activity with neat diagrams…Please post some other concepts about local service and remote service…
    🙂

  8. Yeah! veeery nice explanation, thanks a lot! really.

  9. I’m a new member.

  10. Good explanation.Explanation with Diagrams is more meaningful.You have done good job.
    Thanks…

    If you have time can you please post an article regarding implementation of life-cycle methods in an android application….thanks in advance.

  11. blog09ats

    Hi, you’re right. The screen rotation, by default, involves a destruction of the activity instance and it’s recreation. I’ve updated the post to add such info.

    Those methods are also called in the UI thread, and so, if you’re onCreate or any others methods are pretty complex and long, the user experience may be really bad as he will see a ‘lag’ during the screen rotation.

    Regards,

    Clement

  12. studentdeveloper

    Great artikle, but maybe you should mention, that a screen rotation destroys and recreates the hole activity. So destroy() and onCreate() will be called + all other methods invoked by these ones. Furthermore the methods onRestoreInstanceState(Bundle state) and onSaveInstanceState(Bundle outState) can be used to store and register the screenchange and keep track on your data 🙂

Discover more from akquinet - Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading

WordPress Cookie Notice by Real Cookie Banner