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.
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.
Singleton with context in Android
You are right not to save main activity context into the singleton because of memory leaks. If you need constant context inside your singleton, use getApplicationContext(). This can be safely saved. Note though that this context is not useable for most gui-related functions. In rare cases you need activity level context inside singleton, pass calling activity context to singleton's method without saving
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
Use application class as singleton
Very good @CommonsWare's answer... just complementing...
I believe it is a bad solution because it can cause a Memory Leak... Even though it is very very difficult to happen it... to use a static reference to a -- As commented below, it cannot cause any memory leak in this case. Context
instance is very bad because this Application
instance will not be cleaned when ActivityManagerService destroys it due to that static reference...
I don't like this kind of solution... it's safer to use the Context
directly than it (e.g. getApplicationContext()
).
obs.: It is also Violating Singleton Pattern because it is not restricting the instantiation of the class and doesn't ensure that there can be only one instance of it... but it is not relevant...
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
When to call activity context OR application context?
getApplicationContext()
is almost always wrong. Ms. Hackborn (among others) have been very explicit that you only use getApplicationContext()
when you know why you are using getApplicationContext()
and only when you need to use getApplicationContext()
.
To be blunt, "some programmers" use getApplicationContext()
(or getBaseContext()
, to a lesser extent) because their Java experience is limited. They implement an inner class (e.g., an OnClickListener
for a Button
in an Activity
) and need a Context
. Rather than using MyActivity.this
to get at the outer class' this
, they use getApplicationContext()
or getBaseContext()
to get a Context
object.
You only use getApplicationContext()
when you know you need a Context
for something that may live longer than any other likely Context
you have at your disposal. Scenarios include:
Use
getApplicationContext()
if you need something tied to aContext
that itself will have global scope. I usegetApplicationContext()
, for example, inWakefulIntentService
, for the staticWakeLock
to be used for the service. Since thatWakeLock
is static, and I need aContext
to get atPowerManager
to create it, it is safest to usegetApplicationContext()
.Use
getApplicationContext()
when you bind to aService
from anActivity
, if you wish to pass theServiceConnection
(i.e., the handle to the binding) betweenActivity
instances viaonRetainNonConfigurationInstance()
. Android internally tracks bindings via theseServiceConnections
and holds references to theContexts
that create the bindings. If you bind from theActivity
, then the newActivity
instance will have a reference to theServiceConnection
which has an implicit reference to the oldActivity
, and the oldActivity
cannot be garbage collected.
Some developers use custom subclasses of Application
for their own global data, which they retrieve via getApplicationContext()
. That's certainly possible. I prefer static data members, if for no other reason than you can only have one custom Application
object. I built one app using a custom Application
object and found it to be painful. Ms. Hackborn also agrees with this position.
Here are reasons why not to use getApplicationContext()
wherever you go:
It's not a complete
Context
, supporting everything thatActivity
does. Various things you will try to do with thisContext
will fail, mostly related to the GUI.It can create memory leaks, if the
Context
fromgetApplicationContext()
holds onto something created by your calls on it that you don't clean up. With anActivity
, if it holds onto something, once theActivity
gets garbage collected, everything else flushes out too. TheApplication
object remains for the lifetime of your process.
Related Topics
Setting Singleton Property Value in Firebase Listener
How to Read a Text File in Android
Why Do I Get "Failed to Bounce to Type" When I Turn Json from Firebase into Java Objects
How to Round a Number to N Decimal Places in Java
"Implements Runnable" VS "Extends Thread" in Java
Java.Util.Nosuchelementexception - Scanner Reading User Input
How to Remove Repeated Elements from Arraylist
How to Get a File'S Media Type (Mime Type)
Java.Text.Parseexception: Unparseable Date: Java.Text.Dateformat.Parse(Dateformat.Java:579)
Failed to Resolve Com.Google.Android.Gms Play-Services-Auth:11.4.0
Distinguishing Between Java Threads and Os Threads
How to Create an Executable Jar With Dependencies Using Maven
Converting Iso 8601-Compliant String to Java.Util.Date