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.

Advertisements