In the below example code from my project, android studio warns me that this is a memory leak. Is Android Studio right?
Application class is singleton thus i thought that it is good to store it in my class. What is your suggestions?
public class MyApi {
private static MyApi instance ; // Compiler gives the following warning: Do not place Android context classes in static fields (static reference to MyApi which has field context pointing to Context); this is a memory leak
private Context context; // i need this context inside MyApi class.
public static MyApi getInstance() {
return instance;
}
public static void init(Context context) {
instance = new MyApi(context);
}
private MyApi(final Context context) {
this.context = context;
}
}
public class App extends Application{
#Override
public void onCreate() {
MyApi.init(this);
}
}
Lint sees you store a Context in a static. It does not know which kind of context it is.
If it was an activity context, then it would be super easy to leak. Application context is an application-scoped singleton and it does not cause a leak. You can ignore or suppress this warning if you want to.
static state is kind of an anti-pattern so most of the time you're better of avoiding it though.
Related
I am using Context object inside non-Activity, it is working perfectly but the problem is it shows warning
That is where I am using the context object
Here is the result of inspection
You can use WeakReferences for this case. something like this:
public class ContactsTask {
private WeakReference<Context> weakContext;
public ContactsTask(Context context){
weakContext = new WeakReference<>(context);
}
public void doSomething(){
if (weakContext!=null) weakContext.get() ... //return context without leaks
}
}
I am trying to access application resources, (string resources to be specific) from a Singleton class. Being Singleton, this class cannot hold any reference to Context objects (to prevent memory leak). While I was looking for other implementations on the net, I came across this two implementation:
Create a static context in Application class and use it across the app.
Pass context as a parameter to the method that requires it.
I don't want to use the fist one as it also uses a static reference to Context object. I understand that it's ok to have it statically in the Application class of android, but still it looks like a hack.
The second implementation is useless since i don't have any instance of context which I can pass to the someOtherMethod of the singleton.
So I came up with following implementation where I make my Singleton abstract to override its context requiring methods (for ex. getString(int resId) in the code below) when I initialize the singleton instance.
I am curious to know if this can lead to any memory leaks now?
Where am I confused with this approach:
--> The reference to context in the Overridden getString is final. I am not sure if that can cause a memory leak or not.
public abstract class SingletonClass{
.
.
.
private static SingletonClass sInstance;
private SingletonClass(Context paramContext) {
// constructor code
}
public static SingletonClass getInstance(final Context context) {
if (sInstance == null) {
sInstance = new SingletonClass(context){
#Override
public String getString(int resId) {
return context.getString(resId);
}
};
}
return sInstance;
}
public abstract String getString(int resId);
.
.
.
private void someOtherMethod(){
//uses above getString()
}
}
Your approach does have a memory leak. The first context passed into getInstance will never be garbage collected, since your anonymous class holds a reference to it. (and there is a static reference to the anonymous class). e.g., if you call getInstance(Activity), that activity will remain in memory until the process is killed!
Fortunately there is a pretty easy fix to get rid of the memory leak. You can safely hold onto the application context (context.getApplicationContext), which is basically a singleton context for lifetime of the app.
public static SingletonClass getInstance(Context c) {
if (sInstance == null) {
sInstance = new SingletonClass(c.getApplicationContext());
}
return sInstance;
}
You can depend on activity lifecycle, and require activities to pass reference to your singleton object in onResume method, and clean it in onPause.
protected void onResume() {
super.onResume();
Singleton.getInstance().onResume(this);
}
protected void onPause() {
super.onResume();
Singleton.getInstance().onPause();
}
Also, you can refresh the instance of Context and hold it in WeakReference:
class Singleton {
private WeakReference<Context> mContext;
private boolean hasContext() {
return mContext != null && mContext.get() != null;
}
public static Singleton getInstance(Context c) {
//do your singleton lazy
if (!sInstance.hasInstance()) {
sInstance.mContext = new WeakReference<>(c);
}
return sInstance;
}
}
Second case could hold a reference to finishing activity, so i don't suggest it.
I have created one class that is extending Application class and using the static method to expose the contexts as follows. moreover i am using many util methods like checking internet connections e.c.t into this class.
Problem is i am not sure
how safe is to use following method to expose the application level context
would this create any memory leaks when i will use App.getAppContext() method
public class App extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = this.getApplicationContext();
}
public static Context getAppContext() {
return mContext;
}
}
Android documentation for Application states:
There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality [i.e. maintain global application state] in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.
My request is: Can you explain, and provide code sample that implements the above suggestion for maintaining global state.
Note that there is already a suggestion that recommends subclassing Application:
How to declare global variables in Android?
Thank you.
Correction to StinePike's answer regarding context in the ApplicationState. In the code posted the context passed in to the application state is held on to. If the context passed in is an activity or similar unit then the activity would be leaked and prevented from being garbage collected.
The android documentation for the Application class states you should "internally use Context.getApplicationContext() when first constructing the singleton."
public class ApplicationState {
private Context applicationContext;
private static ApplicationState instance;
private ApplicationState(Context context) {
this.applicationContext = context.getApplicationContext();
}
public static ApplicationState getInstance(Context context) {
if(instance == null) {
instance = new ApplicationState(context);
}
return instance;
}
}
If I am not wrong your are trying to save global variables without extending Application. If so you can do two things
if you don't need any context then you ca simply use a class with static members like this
public class ApplicationState {
public static boolean get() {
return b;
}
public static void set(boolean a) {
b = a;
}
private static boolean b;
}
And if you need a context but you don't want to extend Application you can use
Public class ApplicationState {
private Context context;
private static ApplicationState instance;
private ApplicationState(Context context) {
this.context = context;
public static ApplicationState getInstance(Context context) {
if (instance == null) {
instance = new ApplicationState(context);
}
return instance;
}
public void someMethod(){}
}
So you can call some method like this
ApplicationState.getInstance(context).somemethod();
I'm considering creating a singleton wrapper for a Context so my model objects, if necessary, can open and read from a database connection. My model objects do not have access to a Context, and I'd like to avoid needing to pass a reference to a Context from object to object.
I was planning to place into this singleton a reference to the Context returned by Application.getApplicationContext(). This singleton object would be initialized in my custom Application instance before anything else would need to or have a chance to use it.
Can anyone think of a reason to not do this?
I would urge you to think about what reasons you have for not passing a reference of the application context into your model objects.
There are various well-documented disadvantages of using singletons. I won't go into detail here but you might want to consider:
How singletons limit your ability to properly unit-test your application.
Singletons hide dependencies between different entities in the code- you cannot determine dependecies from inspecting the interfaces.
You have no real control over the lifetime of a singleton, it could exist for the lifetime of your application. Do you really want to potentially hold a DB connection for the lifetime of your app?
The thread safety of your singleton.
If you valid reasons for not passing a reference to a context to other parts of your application then perhaps you should consider some other pattern- a factory might be one possible solution, a service another.
I'm not sure I get your idea, but here's what's been working for me:
public class MyApp extends Application {
private static MyApp instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
// ...
}
public static MyApp getInstance(){
return instance;
}
// misc helper methods
}
Pasted here to preserve formatting.
public class ContextContainer
{
private static boolean initialized;
private static Context context;
private ContextContainer()
{
//
}
public static synchronized void setApplicationContext(Context context)
{
if (!initialized) {
ContextContainer.context = context;
initialized = true;
} else {
throw new RuntimeException("ApplicationContext has already been set!");
}
}
public static synchronized Context getApplicationContext()
{
return context;
}
}