Is it safe to keep the context of an application in a Singleton?

3

I have the custom of keeping the context of an Android application saved within a Singleton as follows:

public class Global {
    private static Global instance;
    private Context context;

    public Global() {
    }

    public static Global getInstance() {
        if (instance == null) {
            instance = new Global();
        }
        return instance;
    }

    public Context getContext() {
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }
}

That way, whenever I need to use something that needs a context (for example, a code inside a fragment), I do it in the following way:

Global.getInstance().getContext();

My question is: is this safe from the point of view of an application? I ask this because I usually define the context as the first Activity used in the application and do not change the context during the application. Can this lead to an error? Is this practice acceptable / commendable, or should I get context every time I need to use it?

    
asked by anonymous 05.03.2015 / 19:31

3 answers

4

It is not a good idea to even have a singleton context variable because there are several types of context and each of them allows something that another can not allow. One of the best ways to at least improve whenever you use context and that whenever you get it from somewhere do:

context.getApplicationContext();

because the applicationContext is already a singleton. See this article on the different Context types for the different components (Activity, Service, BroadCastReceiver) and when to use each of them.

link

    
06.03.2015 / 00:13
1

Your class is more or less. But the first big problem I see is the builder being public. With that, someone just invoke it and your singleton will no longer be a singleton.

There is another big problem too: Synchronization. Just two Threads invoke getInstance() or setContext(Context) at the same time and with a little luck you can end up with two singleton instances, or with two different contexts.

In addition, there is an important question: Why instantiate the class Global in lazy ? It has no heavy resources to be instantiated in the constructor. With this your class looks like this:

public class Global {
    private static final Global instance = new Global();
    private Context context;

    private Global() {
    }

    public static Global getInstance() {
        return instance;
    }

    public synchronized Context getContext() {
        return context;
    }

    public synchronized void setContext(Context context) {
        this.context = context;
    }
}

And so it's already cool, especially if you want to add more things to your singleton.

Something that worries me a bit is this setContext() . Ideally, your singleton is immutable, and once properly configured, it will never be tweaked. But before that comes the question: How do you get Context ? There are several contexts in android, what context exactly are you storing in your singleton and why do you need to do this?

In addition, depending on the case (not in every singleton), you can leave your static methods, eliminating the need to have getInstance() :

public class Global {
    private static final Global instance = new Global();
    private Context context;

    private Global() {
    }

    public static synchronized Context getContext() {
        return instance.context;
    }

    public static synchronized void setContext(Context context) {
        instance.context = context;
    }
}

And then you would only use Global.getContext(); instead of Global.getInstance().getContext(); . Also, you would never see an instance of Global outside of the Global class itself (which is fine since it probably would not make sense).

Returning to the multithreading issue, to avoid having the synchronized methods you can make the instance variable context be volatile :

public class Global {
    private static final Global instance = new Global();
    private volatile Context context;

    private Global() {
    }

    public static Context getContext() {
        return instance.context;
    }

    public static void setContext(Context context) {
        instance.context = context;
    }
}

Another possibility is to use AtomicReference<Context> as the class field:

public class Global {
    private static final Global instance = new Global();
    private final AtomicReference<Context> contextRef = new AtomicReference<>();

    private Global() {
    }

    public static Context getContext() {
        return instance.contextRef.get();
    }

    public static void setContext(Context context) {
        instance.contextRef.set(context);
    }
}
    
06.03.2015 / 00:10
1

As already stated, the context of Activity should not be kept beyond the life cycle of Activity itself. The ideal context to be saved is the overall context of the application.

I have a suggestion for you to get the global context at any time. For this you should extend the class Application :

public class MeuAplicativo extends Application {

    private static MeuAplicativo mInstanciaDoAplicativo = null;

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

    public static MeuAplicativo getInstance() {
        if (mInstanciaDoAplicativo == null) {
            throw new IllegalStateException("Este método não pode ser chamado antes da instância do aplicativo ter sido criada. Por exemplo, evite chamá-lo dentro de um método ou bloco estático.");
        }

        return mInstanciaDoAplicativo;
    }
}

exception is very difficult to do. In any part of your implementation the global context will already have been created, except in static blocks, which are a very unlikely case of context usage.

Do not forget to inform the application class in AndroidManifest.xml :

<application
    android:name="com.pacote.MeuAplicativo">
    ...
</application>

Once you've done this, you can access the global context at any point in the application like this:

MeuAplicativo.getInstance();
    
02.09.2015 / 02:38