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

Asked

Viewed 126 times

3

I have the habit of keeping the context of an Android app saved inside 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, code within a Fragment), I do it this way:

Global.getInstance().getContext();

My question is, is this secure from an app’s point of view? I ask this because I usually define the context as the first Activity used in the application and do not change the context in the course of the application. Can this create an error? This practice is acceptable/recommended or should I get the context every time I need to use it?

3 answers

4


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

context.getApplicationContext();

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

http://possiblemobile.com/2013/06/context/

  • I agree with Rio. And I add that it is never good to maintain an instance of Context because it’s a heavy object, especially if it’s a Activity. Often the calls to methods depend on the life cycle, which is different for each one.

  • Thanks Wakim! if you see the link at the end of the reply.. there is a table that shows the capabilities of each type of context

  • Thanks, I didn’t know much about the context, now I have a better idea of how to work with him.

1

As has already been said, the context of Activity shall not be maintained beyond the life cycle of the Activity. The ideal context to save is the global context of the application.

I have a suggestion for you to get the overall 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;
    }
}

To Exception It’s very difficult to happen. In any part of its implementation the global context will already have been created, except in static blocks, which are a very unlikely case of context use.

Don’t forget to inform the app class on AndroidManifest.xml:

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

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

MeuAplicativo.getInstance();

1

Your class is more or less. But the first big problem I see is the builder being public. With this, just someone summon you and your Singleton will no longer be a Singleton.

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

In addition, there is an important question: Why to instantiate the class Global so Lazy? She has no heavy resource to be instantiated in the constructor. With that her class gets 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 intend to add more things in your Singleton.

One thing that worries me a little bit is this setContext(). The ideal is that your Singleton is immutable, and once properly configured, it would never be touched again. But before that comes the question: How do you get the Context? There are several contexts on android, which context exactly you are storing in your Singleton and why you need to do this?

Moreover, depending on the case (not at all and any Singleton), you can leave your methods static, eliminating the need to have the 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’d just use Global.getContext(); instead of Global.getInstance().getContext();. Also, you would never see an instance of Global out of class Global (which is great, since that probably wouldn’t make sense).

Returning to the question of multithreading, to avoid having the methods synchronized you can make the instance variable context was 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 field of class:

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);
    }
}
  • In your example, it would be better to use "double-checked-Locking".

  • @wyrel I disagree, it would either be wrong or unnecessarily complex to work: http://www.cs.umd.edu/~Pugh/java/memoryModel/Doublecheckedlocking.html

  • Normally Context is an Activity. But it seemed to me quite wrong to do so, hence the question. Could you tell me what the meaning of this Synchronized is? I’ve never seen it anywhere else.

  • 1

    @RenanLazarotto http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html e também http://javarevisited.blogspot.com.br/2011/04/synchronization-in-java-synchronized.html

  • 1

    @Victor, thank you! A very interesting reading, I didn’t even know it!

  • @Victor, in the link you posted, look at the excerpt from "Fixing Double-Checked Locking using Volatile". I personally prefer double check Locking to synchronizing an entire method.

  • @wyrel Yes, but in this case you are changing the synchronized for volatile. In the case of a simple getter and Setter until there is no problem and the volatile It’s actually a little bit better, but if the field you’re manipulating is something more complicated, it changes shape. However I have an even better solution: use AtomicReference.

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.