Android and concurrency

By default, all operations in Android run on the main UI thread. However, for long operations, such as network calls, it is best to execute them concurrently in the background to avoid blocking the user. Thread is an abstraction provided by Java for this exact purpose.

Yet, it is often the case that background operations need to update the UI either during or at the end of the task. Since Android UI toolkit is not thread safe, worker threads can only update it by calling runOnUIThread() or using a Handler to schedule an action to be performed on the main thread in the future.

AsyncTask is an abstraction provided by Android. It is similar to Thread, but since it is managed by the android operating system, it allows you to directly manipulate the UI from it. Moreover, AsyncTask is usually used for this convenience and Thread is often used for background tasks agnostic to the UI thread.

There are are three infamous pitfalls that need to be considered when using Thread or AsyncTask for Android:

  1. Since they do not follow Activity’s life cycle, the worker thread can continue to run even if an Activity is destroyed (i.e. from screen rotation). And so the thread’s subsequent attempt to update the former, non-existent instance of the activity will likely throw an exception.
  2. If you choose to create a class that extends these abstractions as an anonymous, local or inner (non-static) class of an Activity, you can potentially produce a memory leak if the thread runs much longer than an activity instance’s expected lifetime (as determined by the operating system due to memory constraints). This is because in Java, these types of classes hold an implicit reference to its outer class instance, so it’ll prevent the activity from being garbage collected.
  3. Similarly, threads can be leaked if developers forget to “close” them, because active threads are not eligible for garbage collection in Java.

Lastly, Service is another abstraction used in Android for concurrency. It is different from Thread and AsyncTask in the sense that it is not bound to an activity. Moreover, the background task can run even if the device is asleep. For instance, consider a music player app. With Service, music can continue to play even when the app is minimized or the device is turned off. With the previous abstractions, the music will only play when the app is active. Note that Service runs on the UI thread, so a hybrid of Service and Thread or AsyncTask should still be used for longer tasks.