Effective Java #3: Be Monoelvistic like the Singleton

I’ve added a new page in the Joshua Block Tribute section of my blog:

Enforce the Singleton

Elvis, courtesy of the Britannica Blog.

Elvis, courtesy of the Britannica Blog. See also elvis.com.

Singletons have a unique property: there is only one instance of a singleton in the whole system as far as the application is concerned.

On Android many people make the mistake of believing the Activity or any object returned by a getContext() call is a singleton.  But in fact it’s not.  It can be created, re-created, mocked, and otherwise destroyed at any time.

I like to describe Context as just a “gate keeper” that ensures you have a valid application running when you do things like save files to the disk or perform networking.

As far as I know the only kind of Context that is guaranteed to be a singleton on Android is the Context object returned by Context.getApplicationContext().

This is the recommended way to get a global context on Android.  But there is another way as well, and it can serve as an in-memory data store for simple data items.  If you specify an Application derived object in your Android manifest (see the javadocs), you can safely get a singleton object that is unique as long as your process is active:

public class MyApplication extends Application{
     private static MyApplication APP;

     public static String someValue;

     @Override
     public void onCreate(){
          THISAPP = (MyApplication)this;
     }

     /**
      * Very handy to have around when a thread doesn't have a context:
      *
      * new Thread{
      *    public void run(){
      *         String prop = MyApplication.getApp().getPreference("MYPROPERTY", null);
      *         ...
      *    }
      * }
      *
      */
     public void getPreference(String key, String defaultValue){ 
        SharedPreferences prefs = getDefaultSharedPreferences(this);
        return prefs.getString(key, defaultValue);
     }

     /*
      * Must use static factory form in this case because onCreate
      * is the only place to get a valid reference.
      *
      * Call 
      *            MyApplication myApp = MyApplication.getApp();
      * to get the application singleton with global scope.
      *
      */
     public static final MyApplication getApp(){
          if (THISAPP == null)
             throw new  IllegalStateException(
                  "the application has not been initialized");

          /*
           * THISAPP can be used to store some static data that multiple
           * activities can access.  Watch out for a different 
           * instance in your service, however - it may be running in 
           * a different process!
           */
          return THISAPP;
     }
}