Updating an Android Activity from a Background Thread

In the last post, I talked about the difference between a service and a background thread in Android. In this post I want to talk about updating an activity from a background thread that runs in the application context. As you probably know by now, a background thread is needed whenever you want to perform a task that could block the user interface.

Consider the following example. You are writing an application that will load some non-crucial information from a server. This should naturally be done in a background thread. You decide that the user should not have to wait for the information to be loaded before the first activity starts. The user can already use the application and can navigate away from the main activity by the time that the background thread has finished its task. For this reason the background thread should not be limited to the activity. The design choice is to start the background task in your application class.

public class UpdaterApplication extends Application { public void onCreate() { super.onCreate(); Thread thread = new Thread(new Runnable() { @Override public void run() { loadDataFromServer(); }}); thread.start(); } private void loadDataFromServer() { // ... // load data from server // this could take some time // ... // How do we update the current activity? } }

Note that we have to include the line

android:name="UpdaterApplication"

in the definition of the application in the manifest.

But you face a problem. The Application object has no information about the currently visible Activity. How will you notify the current activity once the task has loaded the information from the server? Somehow you have to find a mechanism by which the application object knows about the current activity and can notify it.

The first thing to remember is that the application object is a singleton. This means that we can access the same application object from any activity via getApplication(). Our plan is to provide a method for an activity to register itself with the application. This activity should then be updated when the data has been loaded from the server. We first define an abstract Activity class.

public abstract class UpdatableActivity extends Activity { public abstract void updateActivity() {} }

The UpdatableActivity declares one abstract method called updateActivity(). Any class extending UpdatableActivity should implement this method. This looks very much like an interface. The reason we have chosen to create an abstract class will become apparent later.

We now can provide a method in the application to allow registering the current activity.

public class UpdaterApplication extends Application { private UpdatableActivity currentActivity = null; public void setUpdatable(UpdatableActivity updatable) { this.currentActivity = updatable; } // ... }

The method setUpdatable() will allow the UpdatableActivity to register itself with the application. To automate the process, we add two methods to UpdatableActivity.

public abstract class UpdatableActivity extends Activity { public abstract void updateActivity(); @Override protected void onStart() { super.onStart(); UpdaterApplication app = (UpdaterApplication)getApplication(); app.setUpdatable(this); } @Override protected void onStop() { UpdaterApplication app = (UpdaterApplication)getApplication(); app.setUpdatable(null); super.onStop(); } }

In the onStart() method the activity registers itself by calling app.setUpdatable(this). We also need to make sure that the activity de-registers itself again when it is not active. This is crucial to avoid memory leaks by keeping references to unused objects. To achieve this we call app.setUpdatable(null) in the onStop() method.

Now we are ready to call the update() method of the current activity from the application object. It is tempting to just write the following in UpdaterApplication.

private void loadDataFromServer() { // ... // load data from server // ... // A RECIPE FOR DISASTER!!! if (currentActivity != null) currentActivity.updateActivity(); }

There are two issues here, both having to do with multi-threading. Firstly, anyone implementing the updateActivity() method in UpdatableActivity would probably expect it to run in the UI-thread. But we are calling it from the background thread. To conform to the expected behaviour we add another method in the UpdatableActivity.

public final void updateActivityFromBgThread() { runOnUiThread(new Runnable(){ @Override public void run() { updateActivity(); }}); }

This makes sure that updateActivity() is called on the UI-thread. We marked the method as final to avoid any accidental overriding. Then we change the call from the application to

if (currentActivity != null) currentActivity.updateActivityFromBgThread();

This fixes the first issue. The second issue has to with the fact that the background thread could be interrupted between checking (currentActivity != null) and calling the method on currentActivity. By the time the method is called the main thread could have set its value to null, resulting in a NullPointerException. We clearly need to synchronize the setUpdatable() method with the method that calls updateActivityFromBgThread(). On the other hand we can’t synchronize the whole loadDataFromServer() method because this would mean that no activity can register itself while the background task is running. We need to extract the call to update the current activity into another synchronized method. The complete code of the finished UpdaterApplication class now looks like this.

public class UpdaterApplication extends Application { private UpdatableActivity currentActivity = null; public synchronized void setUpdatable(UpdatableActivity updatable) { this.currentActivity = updatable; } private synchronized void updateCurrentActivity() { if (currentActivity != null) currentActivity.updateActivityFromBgThread(); } private void loadDataFromServer() { // ... // load data from server // ... updateCurrentActivity(); } public void onCreate() { super.onCreate(); Thread thread = new Thread(new Runnable() { @Override public void run() { loadDataFromServer(); }}); thread.start(); } }

For reference, here is the complete code of the UpdatableActivity class

public abstract class UpdatableActivity extends Activity { public final void updateActivityFromBgThread() { runOnUiThread(new Runnable() { @Override public void run() { updateActivity(); }}); } public abstract void updateActivity(); @Override protected void onStart() { super.onStart(); UpdaterApplication app = (UpdaterApplication)getApplication(); app.setUpdatable(this); } @Override protected void onStop() { UpdaterApplication app = (UpdaterApplication)getApplication(); app.setUpdatable(null); super.onStop(); } }

Any activity that needs to be notified of completion of the background thread can now simply extend from UpdatableActivity and implement the updateActivity() method. The method is called on the UI-thread and can, therefore, be used to directly manipulate the user interface.

Notes

There are two things to note about the above approach. If your application is larger, and the application object is used to perform multiple different tasks, these tasks should be transferred to individual singletons. In fact, the user manual for Application states

There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way.

I will talk about implementing your own singletons for Android applications in a future post.

Secondly, if you are updating content regularly you might also be interested in notifying the user, even if your application is not currently in the foreground. In this case you should consider using services.

Follow the author