views:

144

answers:

3

I am building a singleton service in Java for Android:

final class MyService {

    private static Context context = null;
    // ...

    private MyService() {
        myObj = new MyObj(context);
            // ...  
    }                       
    private static class Singleton {
        private static final MyService INSTANCE = new MyService();
    }

    /**
     *  a singleton - do not use new(), use getInstance().
     */
    static synchronized myService getInstance(Context c) {
        if (context == null) context = c;
        return Singleton.INSTANCE;
    }

To improve performance I use static variables and methods throughout.

My questions:

  • for objects, new() ensures the opportunity to initialize that object before its use. Is there an equivalent for a static class? For example: when static methods depend upon earlier initializations - such as as for 'context' above - is there a means of ensuring that the initialization happens first? Must I throw an exception if they have not?

  • as this is all static I expect that there is only the class itself: is that a singleton by definition? Is the recommended code above just the way of preventing instantiation of objects?

Thanks in advance!

+2  A: 

That's certainly one way to create a singleton. I think you could do away with the static helper; going static for performance reasons really doesn't save much and it obfuscates the code.

I think your real question is whether construction of a static can be delayed until context is not null. The answer depends on the VM; in most "vanilla" Java VMs, INSTANCE would not actually be instantiated until the first call to GetInstance(); statics are lazily evaluated for better startup performance. That means that as long as context got something else before the first call to GetInstance(), you're good.

However, forgive me but I do not know if this behavior differs in the Android VM, which is technically not a "real" Java VM (or so says Sun/Oracle). To be on the safe side, if context doesn't have an instance by the time the static constructor is called, I would look into resolving one in the static constructor using a static factory method or a simple IoC, before instantiating INSTANCE.

KeithS
I am composing objects and within them I instantiate and use android classes that require context (e.g. getSharedPreferences). I was using a singleton 'with static' model, thus exposing methods for use without any prior call to getInstance() and the initializations it performs. It seems my first step forward is to abandon 'static' so that getInstance() is ensured - and to study up on when it is proper to use it. Thanks lots to all for helping debug my broken concept!
DJC
The VM spec is very specific about the situations in which statics are initialized. While Dalvik is definitely not Java(tm), it does try to follow the rules laid out in the specification.
fadden
A: 

You can add a static initialisation block to your class

public class MySingleton {
    /* Default constructor */
    private MySingleton() {
    }

    static {
          //Static init stuff...
        }
        ...
    }
}

The static initializer block will be called once for each classloader the class is loaded by - so typically, only once during the life of the process.

Finally, by definition your singleton is only going to be created once so there will be no performance benefit from using static helpers to do the job.

Robert Christie
Robert, according to my O'Reilly, "if a method is declared with the final{or static] modifier, ... the compiler knows that there is only one version of the method, and dynamic method lookup is not necessary ... and are candidates for inlining ..." Maybe that's what you said? :D
DJC
DJC - Sure, but as this case is initailisation for a singleton, the JIT won't bother inlining the call as it's only called once.
Robert Christie
A: 

There is a flaw in the code. What if you call getInstance() for the second time with a different Context? You would get an instance which is created with the wrong context, namely the one which was passed in for the first time. I am not sure if this is intentional, but this makes at least no sense and it could lead to confusion and nasty bugs.

If the intent is to return the same instance for every specific context, I'd use something like this:

public class Service {
    private static final Map<Context, Service> services = new HashMap<Context, Service>();
    private Context context;

    private Service(Context context) {
        this.context = context;
    }

    public static synchronized Service getInstance(Context context) {
        Service service = services.get(context);
        if (service == null) {
            service = new Service(context);
            services.put(context, service);
        }
        return service;
    }
}

This is called a Multiton. It's kind of a combination of a Factory and Flyweight. the synchronized modifier is pretty expensive when extensively used, you may then consider to use a ReentrantReadWriteLock. The Wikipedia article contains an example.

Back to the singleton story, Wikipedia has several good examples. A basic one which constructs the singleton during class loading and another one which constructs the singleton on first request. But generally, the use of this pattern is discouraged.

BalusC
If a hack is a flaw, then its a flaw :) The intent is exactly to ensure that the context is invariant. Attached javadoc spells out this limitation, and no doubt a check of any incoming context and exception thrown on inequality would be safer, if anything. As the service is being used local to a single process, the first used context is forced effectively final. Suggestions welcome!
DJC
The code in your question indicates that there can be more than one context. You might want to make the context itself a factory or singleton as well and obtain it inside service instead. By the way, this is not necessarily a good design. Have a look at [`java.util.ServiceLoader`](http://download.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html) how to do things properly.
BalusC
thanks lots Balus!
DJC