I started learning about Android Development, and I read about static variables are bad and may leak memory because they are not garbage collectable.
I've used some in certain situations, but I am so concerned it may leak memory.
Can someone please look at my code below and see if they leak memory or not?
MainActivity.java
public class MainActivity extends Activity {
public static boolean IS_ACTIVITY_OPEN;
public static ImageView image;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
IS_ACTIVITY_OPEN = true;
....
....
VoiceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
.....
};
registerReceiver(VoiceReceiver, new IntentFilter(BroadCastReceivers.VoiceIntent));
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(VoiceReceiver);
IS_ACTIVITY_OPEN = false;
}
}
Picture.java
MainActivity.image.setImageBitmap(resizedBitmap);
.....
.....
BroadCast.java
if (!MainAcitivty.IS_ACTIVITY_OPEN) {
//start an activity
Intent intent2 = new Intent(context, MainAcitivty.class);
intent2.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent2);
handler= new Handler();
MyPostDelay = new Runnable() {
#Override
public void run() {
context.sendBroadcast(new Intent(VoiceIntent));
}
};
handler.postDelayed(MyPostDelay, 300);
}
else
{
context.sendBroadcast(new Intent(VoiceleIntent));
}
Thank you very much in advance.
Take a look at Avoiding memory leaks article on the Android Developers Blog. Keeping a static field holding a Context, or any other class that has a (strong) reference to a Context (such as any View) will mean that the garbage collector will not be able to reclaim the storage allocated by the Context. If the Context is an Application, thats OK because they live for as long as your app does and wouldn't be garbage collected anyway. But in case of Views, Context is likely an Activity which should be garbage collected as soon as possible.
That doesn't mean that all static fields will catastrophically leak memory. If they're primitive types, or simple classes, or even more complex classes with weak references to other classes, they might not prevent the garbage collector from reclaiming a lot of memory. But generally having static and especially public static fields is a code smell and should probably be avoided so the code is easier to maintain later.
Related
I know that this is a bad idea to pass context/activity to another object that may live longer then activity and store it, but in my app I really need this. Here is a pseudocode that I'm using
class MyActivity extends Activity implements ActivityDelegate {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MySingleton.getShared().setDelegate(this);
}
void onDestroy() {
super.onDestroy();
MySingleton.getShared().setDelegate(null);
}
}
class MySingleton{
private static MySingleton shared;
private ActivityDelegate delegate = null;
public static MySingleton getShared() {
if (shared == null)
shared = new MySingleton();
return shared;
}
void setDelegate(ActivityDelegate delegate) {
this.delegate = delegate;
}
}
As you can see I always set delegate to null in onDestroy, this is why memory leak should not occur. But as far as I know there are some cases when onDestroy is not called. I guess this only happens when the entire app process is purged and the memory get released anyway. So, my question is - is this safe to pass context/activity to another object and then unset it in onDestroy call or it can still cause leaks in some cases ? Thanks
Is it a good practice to inject a Singleton to a BroadcastReceiver?
More specifically lets assume I have singleton like the following:
#Singleton
public class UnitProvider {
private boolean mIsUsingCelsius = false;
protected SharedPreferences mSharedPrefs;
#Inject
public UnitProvider(SharedPreferences sharedPrefs) {
mSharedPrefs = sharedPrefs;
mIsUsingCelsius = isUsingCelsiusPref(Locale.getDefault());
}
public void refreshCelsius() {
if (!mSharedPrefs.contains(SharedPreferencesConstants.SP_KEY_USE_CELSIUS)) {
mIsUsingCelsius = isUsingCelsiusBasedOnLocale(Locale.getDefault());
}
}
}
And there is a broadcast receiver:
public class DummyReceiver extends BroadcastReceiver {
#Inject protected UnitProvider mUnitProvider;
#Override
public void onReceive(Context context, Intent intent) {
DependencyInjectionService.inject(this);
mUnitProvider.refreshCelsius();
}
}
Actually it works but I am not sure about the performance and possible memory leak that situation may cause. Is there any idea about performance and possible lags that injection may cause?
This should be OK. A BroadcastReceiver instance is only alive for as long as it takes it to return from onReceive. It will be eligible for garbage collection after that as long as you didn't do something silly like hold a reference to it.
Also, you can't really "leak" a singleton object because basically they are expected to last forever!
I know that memory leaks have been nearly done to death on stack overflow, but here goes another question, just to be sure...
I have a singleton class, MyManager which on such-and-such an event notifies listeners that something has changed. This manager manages some 'global' data structures, hence my using it.
public final class MyManager{
private final static MyManager INSTANCE = new MyManager();
private ArrayList<MyManagerListener> mListeners = new ArrayList<MyManagerListener>();
public static void addListener(MyManagerListener l){
if (!INSTANCE.mListeners.contains(l)) INSTANCE.mListeners.add(l);
}
public static void disconnect(){
// Does calling this in Activity's onPause() avoid memory leak?
INSTANCE.mListeners.clear();
}
/// Implementation of Manager stuff which includes call to mListener.doSomething();
}
I then of course have the interface MyManagerListener:
public interface MyManagerListener{
public void doSomething();
}
And then in my Activity, I add the Activity instance to the the manager's mListeners, in my understanding this is creating a static reference to the Activity potentially disrupting the Activity's lifecycle, which is bad.
public class MainActivity extends Activity implements MyManagerListener{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create potential memory leak here.
MyManager.addListener(this);
...
}
protected void onPause(){
super.onPause();
// does calling this fix the potential memory leak?
MyManager.disconnect();
}
#Override
public void doSomeThing(){
//do something
}
}
My question is, does my inclusion of MyManager.disconnect() address the potential issue? I know calling ArrayList.clear() sets all the objects in the list's underlying array to null
You should clearly remove references in onPause() or onStop() instead of in onResume().
Doing so should remove all references to your activity and therefore prevent memory leaks.
So from reading/research about memory leaks it suggests to make all inner classes static to avoid memory leaks. However, by looking at the SDK samples (specifically TicTacToeLib) they implement their callbacks without the use of static inner classes. Will this cause a memory leak? If not, why?
private Handler mHandler = new Handler(new MyHandlerCallback());
private class MyHandlerCallback implements Callback {
public boolean handleMessage(Message msg) {
if (msg.what == MSG_COMPUTER_TURN) {
// Pick a non-used cell at random. That's about all the AI you need for this game.
State[] data = mGameView.getData();
int used = 0;
while (used != 0x1F) {
int index = mRnd.nextInt(9);
if (((used >> index) & 1) == 0) {
used |= 1 << index;
if (data[index] == State.EMPTY) {
mGameView.setCell(index, mGameView.getCurrentPlayer());
break;
}
}
}
finishTurn();
return true;
}
return false;
}
}
Yes, this sample will cause a leak in case it keeps a Message in the queue. But it's not a very severe leak since it is usually limited to a rather short amount of time.
But there is a rather simple way to prevent the leak:
Put the following two classes into your project
/** Callback that decouples the wrapped Callback via WeakReference */
public class SafeCallback implements Handler.Callback {
private final WeakReference<Handler.Callback> mCallback;
public SafeCallback(Handler.Callback callback) {
mCallback = new WeakReference<Handler.Callback>(callback);
}
#Override
public boolean handleMessage(Message msg) {
Handler.Callback callback = mCallback.get();
if (callback != null)
return callback.handleMessage(msg);
// else warn, return true, ..?
return false;
}
}
/** replacement for anonymous inner Handler implementations */
public abstract class SafeHandler implements Handler.Callback {
#Override
public abstract boolean handleMessage(Message msg);
public final Handler get() {
return new Handler(new SafeCallback(this));
}
public final Handler get(Looper looper) {
return new Handler(looper, new SafeCallback(this));
}
}
And now you can use Handler / Callback almost as you used to do but it's no longer leaking.
So either like
public class TestActivity extends Activity {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new SafeHandler() { // << instead of new Handler() {
#Override
public boolean handleMessage(Message msg) {
// handle message
return false;
}
}.get(); // << Notice this added .get()
}
}
or like
public class TestActivity2 extends Activity implements Handler.Callback {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(new SafeCallback(this)); // << wrapped in SafeCallback
}
#Override
public boolean handleMessage(Message msg) {
// handle message
return false;
}
}
The leak problem with Handler is that each Message / Runnable (which is actually wrapped in a Message) knows it's target, i.e. has a hard reference to the Handler or Callback. And if that target is a non-static inner class, it will have an implicit hard reference to the outer class which is typically an Activity.
That means that as long as there are Messages enqueued for your Handler, your whole Activity can't be garbage collected.
To solve this issue that chain of hard references from Message to Activity has to be broken. The SafeCallback class does exactly that by keeping just a WeakReference towards your Activity.
That means, the Message has now a hard reference to SafeCallback but the part bind there can now be garbage collected. In case that happens Handler.Callback callback = mCallback.get(); will turn out null and the Message is simply discarded. There is no more useful target anyways. It is still leaking the SafeCallback itself but that's a pretty much empty class so it won't lead to problems.
I would approach it from the standpoint of what use case are you trying to solve, and not what the language itself is doing. If you "nested class" (not inner class because inner classes can't be static) needs to be able to call non-static methods on its parent class, or read non-static members, then you don't have much choice but to make it non-static. If you can get away with not accessing any of the parent class's non-static resources, then by all means do so (you'll save some memory that way anyways). However, if you're worried about memory leaks and you're going to make the nested class private as in your example, then you really shouldn't have anything to worry about because instances of that class can only be created locally to the parent class (unless you create a static member of the parent class that holds a reference to an instance of the nested class, in which case that object would be around until the parent class gets unloaded by the VM).
In summary, I wouldn't personally worry too much about whether or not your nested class is declared as static or non-static, but focus more on the lifecycle of instances of that class, if you're worried about leaking memory.
I am newbie to both Java and Android, and currently I am confused about "memory leak" in Android, for example: I have 01 Class, 01 Activity and 01 Interface as following:
Class BackGroundWorker is a singleton, which lives as long as the application lives:
public class BackGroundWorker {
private IOnEventOccurListener listener = null;
private static BackGroundWorker instance;
// ....
public void setListener(IOnEventOccurListener pListener) {
this.listener = pListener;
}
// ....
public static BackGroundWorker getInstance() {
//...
return instance;
}
}
The Listener Interface:
public interface IOnEventOccurListener {
public void onEventOccur();
}
And the Listener itself (An activity):
public class ShowSomething extends Activity implements IOnEventOccurListener{
BackGroundWorker bgWorker;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bgWorker = BackGroundWorker.getInstance();
bgWorker.setListener(this);
}
#Override
public void onEventOccur() {
// TODO do something
}
}
Now, according to what Romain Guy mentioned here:
It’s a memory leak, because there’s a reference to the listener (Activity). So Java GC cannot collect the Activity, even when it’s not in use.
I was able to solve that problem by WeakReference – but still wonder:
In this case, when the device needs more memory, according to Android Dev document, it will “kill” the activity if needed - assuming that the Activity ShowSomething is “killed” – then what happens ? (It’s still leak according to Romain Guy, and still “killed” )
I am really confused. Could anybody please explain this ?
Thank you in advanced,
Son
Android will destroy activities that are not on the screen to try to free up memory. However, GC rules still apply, and hence your static reference to the activity prevents the memory from being freed.
Eventually, Android will terminate the whole process. At that point, your leaked memory will be freed. However, in between your activity being destroyed and the process being terminated, you are wasting RAM.
Rather than use WeakReference, please null out the static reference when the activity is destroyed.
I think it would have the leak if we will use:
static BackGroundWorker bgWorker;
instead:
BackGroundWorker bgWorker;