Singletons in Android

There was a surprisingly acrimonious discussion about this, in the Android Developer’s Google Group, recently. Apparently the topic can be confusing.

The issue is that developers occasionally find themselves in need of a singleton. As a pattern, I would say that use of singletons has pretty well been discredited (see, e.g., WhySingletonsAreControversial ). Still, in Android, in a muli-activity application, it is entirely likely that there will be shared data and the desire to store that shared data in some place that is accessible to all application activities.

The problem with Singletons is that they really don’t exist. An instance of some kind of state is only a singleton within some context. A “singleton” on your phone is, clearly, not the same as the instance of the same singleton on my phone. It is even possible that the instance of the singleton on my phone this morning isn’t the same as the instance that was there last night, before I rebooted the phone. This only becomes a problem if the number or lifespan of the singleton surprises someone.

Before Java 5, the typical definition of a singleton, in Java, looked like this:

public final class Singleton {
    private static final Singleton instance = new Singleton();
    public static Singleton getInstance() { return instance; }

    private Singleton() {}
    // ...
}

After Java 5, the recommended implementation of a singleton looked like this:

public enum Singleton {
    SINGLETON;

    // ....
}

The idea is that there is only a single instance of the definition of a class (or enum) so we make that single class instance hold and return the singleton instance of the class. There are tricky extensions of this — usually ways to make it initialize lazily — but this is the gist.

In the Java community, the surprises typically showed up in applications that used multiple ClassLoaders. ClassLoaders are the things that read class definitions from, usually, the file system, and convert them into running code. The surprise comes when a developer doesn’t realize that a class definition is unique per ClassLoader (an object of which they may never even have heard) not per application. The Tomcat application server, for instance, loads webapps (.war files) each in its own ClassLoader. If you make changes to an app and reload it, it gets loaded in a new ClassLoader. If the previous version of the application hasn’t terminated yet it is entirely possible to have two distinct copies of the “same” singleton, one in each ClassLoader. …and that’s surprising.

It is entirely possible to for the same thing to happen, in Android. Android’s similarity to Java absolutely extends to its definition of classes as unique per ClassLoader. Multiple ClassLoaders: multiple instances of your singleton. You can demonstrate this with an app of about 10 lines. At least at this point, though, most Android applications don’t make use of ClassLoader magic and so most developers aren’t getting this particular surprise.

When Android developers want a singleton — perhaps the DAO or a place to remember a running AsyncTask — they might consider keeping the reference to it in a custom subclass of Application. If you read the official documentation for the Application class, though, you’ll find this comment, barred for emphasis:

There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.

This is all fine, so far. But here comes a surprise. The Android OS may completely terminate your application and then recreate it. When the new process is created, your classes are loaded and initialized. Exactly how to describe this terminate/reload event was the source of significant heat in the Google Groups discussion. I’ll resist the urge to poke fun at the guys that insisted that this was not a suprise because the reload happens in a new process [The classes are not reloaded! Rather there is a new "process". What is a "process"? It is an undetectable entity that makes it appear that your classes have been reloaded...] because they are exactly right. An Android application will, frequently, span several processes. This is not something that is common in, say, the desktop world. It may be surprising.

The point is that you cannot put program state into class data members and expect it to stay there. Frequently, this isn’t a problem. If you use a class data member to store a reference to something idempotent, e.g., your DAO, you don’t care which specific instance you happen to use. That is, this code works just fine:

public class MyApplication extends Application {
   private static final MyDAO db = new MyDAO();
   public static MyDAO getDB() { return db; }

   // ...
}

This code, however, won’t work:

public class MyApplication extends Application {
    private static long lastUpdate;
    public static void setLastUpdate(long udate) { lastUpdate = udate; }
    public static long getLastUpdate() { return lastUpdate; }

    // ...
}

… or, at least, you may be surprised to find that getLastUpdate returns 0, even though the code has called setLastUpdate() at some point. Suppose an Activity in the application looks like this:

// ...
setContentView(R.layout.main);
((TextView) findViewById(R.id.myText))
    .setText(String.valueOf(getLastUpdate()));
((Button) findViewById(R.id.myButton)).setOnClickListener(
    new Button.OnClickListener() {
        @Override public void onClick(View v) {
            MyApplication.setLastUpdate(System.currentTimeMillis());
        } } );
// ...

If I push the button, then the Home key and then run a bunch of other apps (on my Nexus S, about a dozen), when I return to this app, the text view will read 0, despite the fact that I definitely pushed the button.

I don’t think anyone is claiming that this is a bug. …and it certainly helps to understand why it happens. As my colleagues point out, it happens because the application survives process boundaries. It is definitely surprising, though, and it puts that quote from the documentation, cited above, in a new light.  Neither the application object nor a static singleton can be expected to hold persistent state for the life of the application! It just won’t work.

What might be a bug is that it is hard to tell when a process ends (thus my jibe about processes being undetectable). I have empirically determined that Application.onTerminate() is not always called, and I have from an authority I respect, that it is, actually, never called (documentation to the contrary). It is probably best to design so that you don’t depend on knowing, anyway. To do that, your static fields must appear to have a value that is constant as of creation. Making them final is a good start.

In some ways, this is the opposite of the problem in Tomcat.  Whereas, in affected web apps, a single object might, at some point in its lifetime, have references to multiple, inconsistent copies a given singleton, in Android a single object, over its lifetime, can’t get a reference to even one instance that stays consistent.  So, don’t be surprised.  It is really easy to create an application in which an Activity, for instance, retains some state for much longer than fields in the Application object, or its own static fields. Nothing new: we’ve all initialized apps from the file system or from a database, before. Usually, though, we do it at “startup” and we have some definite ideas about when “startup” happens.. That’s not how Android works. Serialize your state into a Bundle or Sqlite, if you need to keep it around.

About these ads

About bmeike
Android developer and evangelist in Oakland, CA

11 Responses to Singletons in Android

  1. This exact issue has been plaguing development for a project of mine, and I am still trying to figure out a good approach to rework an app that has been designed around having a big persistent collection of domain objects globally available.
    This post is great!

  2. Markus says:

    Thanks for this clarification. I’m sure I would have run into strange problems if I haven’t read that before, because I already thought about using a singleton for some basic controller behavior in my application.

  3. Bill Beck says:

    I disagree with part of what has been said here. Extending the Application context object is the easiest way to gaining access to a single SQLite database object. This is critical to have when there are multiple threads. Actually, through painful experience, the Android documentation on using SQLite databases is flat out wrong in a few key areas. The example given in the documentation relies on a single activity in a single thread that accesses a SQLlite database instance. There’s nothing obviously wrong with that example until you start dealing with multiple activities and multiple threads. The ONLY way to cleanly fix the problems that crop up is to extend the Application context and initialize the SQLite database object there and create a function that returns a handle to it to any caller that might need it. There may only be one connection open to a SQLite database at any time – otherwise it will throw bizarre exceptions (usually crashes the app) when you least expect it. It also correctly handles synchronization when multiple threads access the same instance and will clean itself up when the Application context is terminated.

    What the Application context and the general-purpose singleton pattern are NOT good for is application state data. Basically, if you want data to persist across two instances of the same Activity, you have to store it somewhere more permanent whether it is external storage (e.g. a SD card), the PreferenceManager, or a SQLite database and keep the two in sync. Extending the Application context is useful when the same data object is needed across all activities but it has to support the fact that the application can be terminated at any time by the Android platform and therefore be capable of reloading the data from permanent storage. The singleton pattern is more useful as a temporary cache for high-performance lookups with a permanent storage mechanism as backup should it “fail” for whatever reason. You can stop Android apps by going into Settings -> Applications -> Your app -> Stop. That forces the Application context to be destroyed. A good rule of thumb is that if your application can’t reasonably survive that, then you’ve written it incorrectly.

  4. Tony Long says:

    Did anyone else experience formatting issues with this blog post? I found that a lot of the text got lost to the right of those nested frames and there was no easy way to scroll horizontally. Maybe it’s just me but I tried with IE & Firefox.

  5. john says:

    “An Android application will, frequently, span several processes.” Do you mean 2 processes for example runs the same Android application at the same time? The doc says (1) “If an application component starts and there already exists a process for that application (because another component from the application exists), then the component is started within that process and uses the same thread of execution.” Please shed some light on this.

    1. http://developer.android.com/guide/components/processes-and-threads.html

    • bmeike says:

      When I say that an application will span several processes, I mean that application state, as perceived by a user, lasts longer than the process that runs the application. If Android needs space for another application it may kill yours and then restart it when the user returns to it. That should be invisible to the user.

  6. Nice post. I only wish I would have found it last year when I had asked, and subsequently answered, this question on S.O.:

    http://stackoverflow.com/questions/19433679/testing-android-application-process-lifecycle-restoring-state-when-empty-proce

    Do you find it more effective opening more apps as apposed to less with long list/scrollviews?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: