Android Singleton with Global Context

Android Singleton with Global Context

Another edit to the question (2016)

Lately (as of 2016 and onward) what I've been doing, and would be my suggestion for any developer, is:

Just use Dagger 2. Wherever you need a Context you do:

@Inject Context context;

and that's it. While at it, inject all the other stuff that would be a singleton.

Edited/improved answer (2014)

because this answer is getting kinda-of popular, I'll improve my own answer with example code of what I've been using lately (as of Jul/2014).

Start by having the application keeping a reference to itself.

public class App extends Application {
private static App instance;
public static App get() { return instance; }

@Override
public void onCreate() {
super.onCreate();
instance = this;
}
}

then on any singleton that needs access to the context I lazy load the singles in a thread safe manner using double check synchronization as explained here https://stackoverflow.com/a/11165926/906362

private static SingletonDemo instance;

public static SingletonDemo get() {
if(instance == null) instance = getSync();
return instance;
}

private static synchronized SingletonDemo getSync() {
if(instance == null) instance = new SingletonDemo();
return instance;
}

private SingletonDemo(){
// here you can directly access the Application context calling
App.get();
}

Original answer

what the documentation is suggesting is to use a normal singleton pattern

 public class SingletonDemo {
private static SingletonDemo instance = null;

private SingletonDemo() { }

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

and include inside it a method like this:

 private Context context;
init(Context context){
this.context = context.getApplicationContext();
}

and remember to call this to initialise the singleton.

The difference between the Application approach and the Singleton approach and why the Singleton is better is on the documentation same functionality in a more modular way

Which android context should i use in singletons?

What you pass in does not matter much. What you use should be the Application context:

public static Database getInstance(Context context) {
if (Database.instance == null)
instance = new Database(context.getApplicationContext());
return instance;
}

Now, if you want to force the caller to supply the Application, you could do that:

public static Database getInstance(Application context) {
if (Database.instance == null)
instance = new Database(context);
return instance;
}

Personally, I like to consider that an implementation detail, hidden by the API.

The Application is a global object, created when the process is created. Therefore it is "pre-leaked", in effect. You cannot leak it further. Other types of Context (e.g., Activity), referenced from some singleton, may result in memory leaks.

See Dave Smith's epic blog post on the roles of different Context objects for more.

Singletons vs. Application Context in Android?

I very much disagree with Dianne Hackborn's response. We are bit by bit removing all singletons from our project in favor of lightweight, task scoped objects which can easiliy be re-created when you actually need them.

Singletons are a nightmare for testing and, if lazily initialized, will introduce "state indeterminism" with subtle side effects (which may suddenly surface when moving calls to getInstance() from one scope to another). Visibility has been mentioned as another problem, and since singletons imply "global" (= random) access to shared state, subtle bugs may arise when not properly synchronized in concurrent applications.

I consider it an anti-pattern, it's a bad object-oriented style that essentially amounts to maintaining global state.

To come back to your question:

Although the app context can be considered a singleton itself, it is framework-managed and has a well defined life-cycle, scope, and access path. Hence I believe that if you do need to manage app-global state, it should go here, nowhere else. For anything else, rethink if you really need a singleton object, or if it would also be possible to rewrite your singleton class to instead instantiate small, short-lived objects that perform the task at hand.

Access the application context inside of a singleton

Context is not good in a ViewModel, not even application context, nor is AndroidViewModel good to use. So you can pass AssetManager into your ViewModel which can then pass it into your ProgramListService, avoiding the need for context, but asset manager is still a bit weird to have in view model.

So you can skip the viewmodel and pass the application context directly into your singleton ProgramListService.

object ProgramListService {
lateinit var application: Application // Add this

...
}

And then from your activity's onCreate or wherever is best for your project,

ProgramListService.application = context.applicationContext as Application

Or like @Tenfour04 suggested application is safe to make a global property

Is setting a global Context in a class bad practice?

If I pass a context in the constructor of an object, could that potentially cause a memory leak if the object being created is a long lived object or a singleton

Yes. For example, if that Context is an Activity, once the Activity is destroyed, ordinarily it would get garbage-collected. But, if you have a reference to it from a static field, it cannot be garbage-collected. Unless you update the field, you will leak the Activity and everything that it references.

Would it be preferable to set the context from getApplicationContext()

Yes. The Application context is a singleton, as you note, one that lives for the duration of your process. In effect, it is "pre-leaked". You cannot leak it further by having another static field point to it.

Android - Google's Contradiction on Singleton Pattern

Since Google is contradicting itself

No, it is not.

The quoted Lint warning is not complaining about creating singletons. It is complaining about creating singletons holding a reference to an arbitrary Context, as that could be something like an Activity. Hopefully, by changing mContext = context to mContext = context.getApplicationContext(), you will get rid of that warning (though it is possible that this still breaks Instant Run — I cannot really comment on that).

Creating singletons is fine, so long as you do so very carefully, to avoid memory leaks (e.g., holding an indefinite static reference to an Activity).

Android, Singleton, context

You can set the context to be you ApplicationContext.
You can create an Application class and implement something like:

yourSingletonClass.getInstance().setContext(this);

This call should be in you application class under the onCreate method.
For more information try this docs:
Android - Application class



Related Topics



Leave a reply



Submit