I have a class which has a static variable which holds Context reference.
public static Context context;
Android studio gives a warning saying that static references to the
Context class leads to memory leaks
If I remove the static keyword, it does not give any warning.
Similar scenario with ContextWrapper class as well.
My understanding is, if we hold a reference to the classes which are related to Context will lead to memory leak. But, Why Android studio does not give any warning for non-static Context references?
I have seen some code examples where they have extended the ContextWrapper class. Whenever they needed the Context, they are accessing through the class which was extended `ContextWrapper'. Will this approach does not lead to Memory leak?
Also, Will memory leaks happens for non-static Context references at runtime? Did I understand it in wrong way? Am I missing something here?
Can anyone give explanation for this?
Thanks in advance.
First things first, let's know about memory leaks and why it happens exactly
Memory leaks occur if there is any data in memory which can't be garbage collected ,Having said that, static variables can't be garbage collected as they stays live in memory throughout the Application life where as non-static variables can be garbage collected once it's enclosing parent is no longer referenced, keeping this in mind we'll see example to explain your question.
Consider class A with a static and non static Variable
Class A{
private static Context iCanNeverBeDestroyed;
// Scope -> Application life, so memory leak can occur
private Context iCanBeDestroyedWithA;
// Scope -> A
private static int itWontMatterWhetherImDestroyedOrNot;
//Even though the scope is Application, due to low memory usage , memory leak is negligible in this case
}
Leakage is calculated based on scope of variable and size in memory together, high memory and greater scope has greater chances of leak, example like Context,Bitmap etc
Related
This question already has answers here:
Non-static variable cannot be referenced from a static context
(15 answers)
Closed 3 years ago.
Here is my method:
public Cursor rawQuery(String sql, String[] selectionArgs) {
try {
return m_db.rawQuery(sql, selectionArgs);
} catch (SQLiteException e) {
reportException(Context.getApplicationContext(), e);
return null;
}
}
Android Studio (3.5.3) complains saying
Non-static method getApplicationContext() cannot be referenced from a static context.
I don't see any static context here. rawQuery is a perfectly good class method of a class which is a wrapper around SQLiteDatabase to save me having to check for exceptions on every call. The exception checks are needed because it's accessing a public database and some other process may have modified it in such a way that my operation fails (for example by dropping one of my tables). It is (currently) only created and its methods called from an Activity. Of course I could pass the calling activity's context, but this is clumsy since it isn't needed most of the time and IMHO it's very poor programming style to include extra arguments in the methods of a wrapper class.
reportException does what its name says and writes a log file or displays a Notification or a Toast, depending on circumstances, and all of these need a context.
I've seen suggestions on the net to create a static instance of a subclass of Application to cache the application context. As commenters have pointed out, this doesn't always work if you need the application context in a class constructor (or anything which is called from one), but I don't expect to do this. My wrapper class is created as needed when I want to access the database. However I'm not sure if tha Application subclassing trick works if I open a database in a background server which may get kicked out of memory when not active and later restarted by the OS. It may be that the only solution is to cache the creator's context in the constructor of the wrapper class: this only requires passing the context once. However I don't much like the idea of keeping a copy of the passed context: it looks inelegant and a potential problem with garbage collection since I have to take care not to use the cached context when creating anything persistent..
However I still don't see Android Studio's justification for complaining in the case shown. I tried removing all the calls to rawQuery and it still complains, so it isn't walking the call tree to look for a non-static context. It looks as if it may be complaining if getApplicationContext is used in any class which isn't a subclass of Activity, which certainly isn't justified.
I don't see any static context here.
The "static context" referred to by the error message is the way you are calling the method: Context.getApplicationContext(). Since you are using the Context class name, this counts as a "static context". You need a Context instance in order to call getApplicationContext().
Of course I could pass the calling activity's context, but this is clumsy since it isn't needed most of the time and IMHO it's very poor programming style to include extra arguments in the methods of a wrapper class.
Yes, I agree that you should keep your argument list as trimmed down as possible. You say that this method is a wrapper around SQLiteOpenHelper which requires a Context as one of its constructor parameters. So presumably your own constructor takes a Context to pass to the wrapped SQLiteOpenHelper instance. One solution is to keep that Context as a field in your class. Then you can just use this.context.
I have written the following class inside one of my adapters. The class gets a view from the activity inside its constructor:
private class ContactViewHolder {
public TextView tvPrimary;
public TextView tvSecondary;
public ImageView imageViewCheck;
public ContactViewHolder(View v) {
this.tvPrimary = (TextView) v.findViewById(R.id.tv_primary);
this.imageViewCheck = (ImageView) v.findViewById(R.id.iv_check);
v.findViewById(R.id.iv_mm_user);
this.tvSecondary = (TextView) v.findViewById(R.id.tv_secondary);
}
}
Will this lead to a memory leak issue?
It seems it might cause a memory leak, as described in this link, regarding a slightly different issue.
So, to sum up, I want to know:
Will this cause a memory leak issue?
If so, why?
If you define a instance like this:
static ContactViewHolder holder = new ContactViewHolder(view);
It will result in a memory leak.
If you define a non-static instance, it has no memory leak.
The reason of leak is as follows:
No-static inner class will hold a reference of outter class instance, so a static holder will hold a reference of your adapter.
As is known to all, static instance can not be collected by GC, it's a gc root, so your adapter can not be collected until the process is terminated.
Memory leaks occures when you dont know/control the lifecycle of the inner class object. Because its not being collected by the GC then the object the holds him doesn't get collected and so on and that causes a memory leak. Basically if your holder only handles with the ui in the simplest way like you wrote then there is no way that he will "outlive" the adapter that holds him then it will not cause a memory leak.
If for some reason you are implementing onClick listener inside the Holder which open an AsyncTask for example then you dont control the life-cycle of the object because it opens a new Thread and might be doing some operation while the adapter is no longer on the UI and that will cause a memory leak.
Conclusion:
Be very aware of the lifecycle of your objects and who owns them.
With this question the main concern is what could be best approach. In my android app an Application class is subclassed mainly for two reasons,
1), Maintaining the global state across the app,
2), Ability to initialise and use SharedPreferences in non-activity classes.
The code is somewhat like this,
public class GlobalApp extends Application {
private static GlobalApp instance;
private Bitmap bitmap;
#Override
public void on create(){
super.on create();
instance = this;
}
public static GlobalApp getInstance() {
return instance;
}
public void setBitmap(Bitmap b) {
this.bitmap = b;
}
}
As you can see, to have access of application context in non-activity class, it has been taken as the static whereas other fields are simply instance variables, in which we are storing Bitmaps, will it cause the memory leak ?
And above approach is best? Or it can be made even better than this?
Please help.
I don't see anything there that could cause a memory leak. However, you needn't store and retrieve the instance like that, you can just do this:
GlobalApp app = ((GlobalApp)this.getApplication());
Just make sure to add the application class name to the manifest. Keep in mind also that just because there isn't a memory leak doesn't mean there isn't a memory problem. If you hold on to too much memory in that Application object, your app could crash with an OutOfMemoryError. Depending on your exact needs you may need to use weak references to avoid that. More info about using the Application class: Using the Android Application class to persist data
From my understanding, Application in Android is a singleton (correct me if I'm wrong) and we always have just one application Context instance.
So, from this perspective, is it a bad practice to save the application Context in my Application class?
Can it lead to a massive memory leak?
Here is an example:
public class MyApp extends Application {
private static Context appContext = null; // <-- here is the thing!
#Override
public void onCreate() {
appContext = this;
}
public static Context getApplicationContextSingleton () {
return MyApp.appContext;
}
}
The reason to do this is globally accessed classes, like PreferencesManager, that mostly have static methods always need a context. So, instead of passing it everytime (or even storing it in an instance, which can be bad), I thought about storing the app context. What are the drawbacks I'm not seeing?
is it a bad practice to save the application Context in my Application class?
It is a code smell.
Can it lead to a massive memory leak?
Having the static data member will not lead to a massive memory leak. Whether your over-use of the Application object will lead to a massive memory leak depends upon where and how you use it.
What are the drawbacks I'm not seeing?
Not all Contexts are created equal. Generally speaking, only use Application when you know specifically why you need the Application context, not for everything.
Dave Smith of DoubleEncore has an awesome blog post covering the differences between types of Context and when to use one over another.
I've been studying up on best practices for preventing Context/Activity memory leaks when creating views, and I can't seem to find a definite answer on what is or is not allowed when it comes to static fields in classes.
Let's say I have a code of this form:
public class MyOuterClass extends Activity{
private MyInnerClass;
MyInnerClass = (MyInnerClass) findViewById(<XML call here>);
MyInnerClass.myXInt = 3;
// onCreate(), onResume(), etc.
public static class MyInnerClass extends SurfaceView implements Runnable{
// Safe variables?
private static int myXInt, myYInt;
private static boolean myBoolean;
// Potentially safe?
private static Canvas myCanvas;
// Definitely bad.
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
}
I am slightly confused on what the JVM actually considers the ClassLoader for MyInnerClass. Technically, since it is a SurfaceView object, it seems like the static variables should always exist once the application has instantiated MyInnerClass one time (which happens when the View is first inflated), and then remain there until the application itself is terminated. If that is the case, what prevents Bitmaps and Canvas objects from remaining open as well and filling up the heap?
The only statement I ever see repeated over and over is that you can't leak static Context like I have shown in the constructor, but it never goes beyond that. Is that really the only thing you can't do?
In Java/Android a static variable or constant will not be garbage collected. It just stays there once the class that holds it is loaded via a class loader. The class loader is afaik always the same for all classes inside your app and its the one that has static references to all your classes (to e.g. MyInnerClass.class). Since the class loader does not go away your classes won't do that either since they are referenced & therefore not garbage collectable.
Like in your example
public class SomeClass extends SurfaceView {
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
That is indeed bad. Even if no reference to SomeClass exists (e.g. the Activity that showed your custom SurfaceView has ended) the static reference to the Context (and any other static variable / constant in SomeClass will remain. You can consider all of them leaked since it is not possible to garbage collect that Context etc. If you have a regular variable reference something then once the instance that contains that variable has no more references to it the whole instance including its references to other things can and will be garbage collected. Java can even handle circular references fine.
For constants you want that to happen and it is usually not bad since the amount of constants and the amount of memory they occupy is not large. Also constants don't (should not) reference other instances that take up large amounts of memory like Context or Bitmap.
Besides the possibility to create memory leaks through static variables you may also create problems if you don't want to have only a single thing for all instances at the same time. For example if you save the Bitmap of your SurfaceView in a static variable you can't have two different images. Even if the two SurfaceViews are not displayed at the same time you could run into problems since each new instance will probably overwrite the old image and if you go back to the other SurfaceView you unexpectedly show the wrong image. I am almost sure you don't want to use static here.
The fact that your inner class is a static class does not mean that you have to use static variables - it just means that it behaves more like a static method since it can't use the instance variables (the ones that are not static) in your class.
To avoid memory leaks you simply should not use static variables at all. There is no need to use them unless you do special stuff (e.g. counting instances of a class). Constants are fine.
This article talks about mutable static fields: http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx. Basically, avoid them and use constants instead.