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.
Related
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
I have two activities, a MainActivity and a secondary activity (e.g.: an about screen), then I have an asynctask which updates the UI on the MainActivity. This part works fine, the asynctask updates the UI by calling a method inside the MainActivity which inflates the UI and sets some values. This method also makes all UI components visible.
What doesn't work is, after going to the About screen and back to the MainActivity, the UI is completely blank. I don't understand why this stops working after navigating back from a different activity, which otherwise works fine.
Can someone please advise?
Here's how I draw the UI. This is how I update it from the thread, and it works, until I go to the about screen:
private void DisplayMainContent()
{
Context context = Util.DataStruct.LoadContext();
Log.d("debug", "DisplayMainContent() loaded a context " + context.toString());
RelativeLayout parent = (RelativeLayout)((Activity)context).findViewById(R.id.action_settings);
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = li.inflate(R.layout.activity_main, parent);
TextView version = (TextView) v.findViewById(R.id.latestVerField);
version.setText(Util.DataStruct.GetVal("version"));
}
little story about vanishing data..
advice:
do not use new activity to achieve this - do your about as dialog or dialog fragment
nice example how to show dialog using fragment
don't use static - instead use singleton pattern
Singletons preserve the conventional class approach, and don't require that you use the static keyword everywhere. They may be more demanding to implement at first, but will greatly simplify the architecture of your program. Unlike static classes, we can use singletons as parameters or objects. Also,you can use singletons with interfaces just like any other class.
where i see problem:
this line is all u need to trace yr mistake:
(i think any other fragment of yr code is irrelevant to yr problem)
version.setText(Util.DataStruct.GetVal("version"));
Explanation why:
Util.DataStruct:
should be singleton with valid hard reference to it eg. in Applictation class or any other which life is longer as the activity u use to display data.
are you aware of the existence of garbage collector?
what i'm trying to point out ? why u should avoid STATIC !?
Code(data) flow:
app launched - initializes static class/variables etc
your variables are feed (via async or else way)
your app is closed by ANDROID OS - regardless of the reason
os recreates "stack"
but not yr variables - they are empty/null/defalt - not referenced by values as they shoud in normal code flow
context:
from where do u use yr DisplayMainContent() ? for what u need context there ?
context should be "anchor" for yr app methods which need it. (it's like certain security stuff - "hi this app fragment belong to me i have the right to modify and view contents - so to do any stuuf u pass nearest context u got - from fragment activity dialog widget etc")
if u can use getContext() eg. ("from parent") - dont use any static one
example:
in fragment:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context context = container.getContext();
}
in adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Context context = parent.getContext();
}
about inflation
- use :
LayoutInflater.from(context).inflate(res,ViewGroup,attachToRoot);
do u use parent in inflation(in fragment doubtless u use in activity doubtful)
for #bcorso:
Do not use more resources than you need.
#TomaszBest sorry, but you really don't know what you're talking
about: Util.DataStruct.GetVal() is calling a static method of the
static class Util.DataStruct, and therefore must return a static class
variable. Static class variables are singletons (only one will ever be
created), and it will not get garbage collected.
An object referenced through a static member variable is strongly referenced until the class is unloaded. A normal ClassLoader never unloads a class, but those used by application do under the right conditions.
If the static field is changed to reference a different object, the original object pointed to by the static field is eligible for GC just like any other object!
The initialization of static variables is covered in Section 2.11 Static Initializers of suns JVM spec. The specification does not define the implementation of Garbage collection - garbage collection rules for static objects will vary depending on your VM.
in sum:
If your class is holding onto this object permanently, it will only be released when the vm exits. Only Classes and interfaces loaded by the bootstrap loader may not be unloaded.
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.
When I'm writing a method or using a member variable, I often find I need to share them across an app. But where should they go?
I can subclass Activity, but that falls over as soon as I use a MapView and am forced to use MapActivity, so not all my activities inherit from my subclass. Is there I way around this?
Where inheritance isn't applicable, I am tending to put generic methods and member variables into a subclass of the Application object, but I'm finding it's creating a mess of code as every class needs to either grab access to the application object through via context, or I have to pass it down.
I suspect I would be better off creating MyApplication.getInstance() and keeping everything in a singleton, instead of passing the application object down through the app classes. but before I wanted to see what you guys had to say.
If you want to access the "Global Singleton" outside of an activity and you don't want to pass the Context through all the involved objects to obtain the singleton, you can just define, as you described, a static attribute in your application class, which holds the reference to itself. Just initialize the attribute in the onCreate() method.
For example:
public class ApplicationController extends Application {
private static ApplicationController _appCtrl;
public static ApplicationController getAppCtrl()
{
return _appCtrl;
}
}
One example with resources: Because subclasses of Application also can obtain the Resources, you could access them simply when you define a static method, which returns them, like:
public static Resources getAppResources()
{
return _appCtrl.getResources();
}
For global methods, use a static Util class with static methods. If you can't use static methods, then the methods shouldn't be global in the first place, and put them in the class that makes sense.
First read this:
How to declare global variables in Android?
Now why you shouldn't use a static singleton. Using a singleton is a the same thing as a global variable. Global variables reduce your maintainability because everywhere you use the global variable you break modularity or introduce global details and assumptions about your overall design. Your program can't have two of these variables because it only looks in one place for it. This means your program can't adapt easily when you have two instances instead of one.
For example, say I have a method called playTurn() and I implement it like so:
public void playTurn() {
globalPlayer.incrementClock();
globalPlayer.doSomething();
globalPlayer.doSomethingElse();
}
Now let's say I want to add a second player to the mix. Uh oh my playTurn() method assumes one player only when it used globalPlayer. If I want to add a second player to the program I have to change that method. Do this a lot and your program is very rigid and inflexible to change. Instead what if I did this:
public void playTurn(Player player) {
player.incrementClock();
player.doSomething();
player.doSomethingElse();
}
Now can do this:
playTurn( player1 );
playTurn( player2 );
I can reuse playTurn() for both player1 and player2 and I didn't have to change it. I just had to change the client of that method.
Most of the time you're being lazy and you want to get a reference to some object, and global variables are fast ways to get references to well known objects. Instead it's better to have one class that resolves the dependencies across your application at start up or the time when it makes sense. Then only that one place understands how your code is put together. For example,
public class Game {
Player player1;
Player player2;
Board board;
public void startGame() {
BlueTooth blueTooth = BlueTooth.getChannel();
player1 = new LocalPlayer();
player2 = new NetworkedPlayer( blueTooth );
board = new Board();
player1.setOpponent( player2 );
player1.setBoard( board );
player2.setOpponent( player1 );
player2.setBoard( board );
}
}
Now everyone has their dependencies, and they don't need to use static variables to find references to things. Also player1 doesn't have to know about details like that player2 is over the network, or that it's apart of a Game. What's important to note is that these objects we're connecting have a long life, possibly the entire program, but if they need to create other things at runtime that's ok for them to do.
Say for example, we need to create multiple players at runtime based on who joins the game. Well we might create a PlayerManager that we can instantiate at startup then create Player objects on the fly. PlayerManager is just a plain old object that we create in Game when we start a new game.
I hope you can start seeing this is a much better way to develop software. You might not understand it right off, but if you think about it will make more sense. It's very subtle change, but very powerful.