Reputation: 2069
In my Activity, I want a floating Action button to appear 1 second after the activity has loaded.
For this, I have created a new Static Method like this.
public class utility {
public static void delayedShowFab(final FloatingActionButton fab)
{
new Handler().postDelayed(new Runnable(){
@Override
public void run() {
fab.show();
}
}, 1000);
}
}
I am calling the method like this:
@Override
protected void onCreate(Bundle savedInstanceState) {
final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
utility.delayedShowFab(fab);
}
Is my code safe from Memory Leak ? Will the new Runnable and the new Handler object destroy themselves automatically after 1 second ?
Upvotes: 0
Views: 611
Reputation: 36045
Yes and no. In normal operation, the references will be cleared and the memory will be freed. Technically there is a memory leak if the Activity is killed before one second has passed. The FloatingActionButton
still holds a reference to its parent Context
. So the Activity
will stay in memory until the Handler processes the message in which case the fab will show, but nothing will visibly happen because the Activity is no longer in view.
This wouldn't have a hugely significant impact on performance because it's only one second. There's just some minor overhead for that one second, but nothing significant. The real issue would be if you extended that time by let's say, 1 minute.
public class utility {
public static void delayedShowFab(final FloatingActionButton fab)
{
new Handler().postDelayed(new Runnable(){
@Override
public void run() {
fab.show();
}
}, 1000 * 60);
}
}
Now, let's say the user just decided to rotate the phone a hundred times within that minute (maybe he's jogging and the screen is on. Who knows?).
Each time the screen rotates, a new Activity is created with a new FAB. You instantly post to the main thread to wait for another minute. Every Activity created is going to exist until this message is run. This means all resources that are contained within that Activity will also exist for that duration.
To remedy this, there are a few options.
Option 1: Cancel the operation.
To keep the API intact. You can return a method in which to cancel the operation. Similar to this:
public class utility {
public interface Cancelable {
void cancel();
}
public static void delayedShowFab(final FloatingActionButton fab) {
final Handler handler = new Handler();
final Runnable runnable = new Runnable(){
@Override
public void run() {
fab.show();
}
};
handler.postDelayed(runnable, 1000);
return new Cancelable() {
public void cancel() {
handler.removeCallbacks(runnable);
}
}
}
}
Then, in the Activity's onDestroy
method, just call cancel
on the returned object.
Option 2: Use a weak reference
WeakReferences are used to hold a reference to an object without including it in the reference count. That way, it will disappear when all references to that object are gone.
So in this instance, you'll hold a WeakReference to the FloatingActionButon
and only call show
if it still exists.
public class utility {
public static void delayedShowFab(final FloatingActionButton fab)
{
final WeakReference<FloatingActionButton> ref = new WeakReference<>(fab);
new Handler().postDelayed(new Runnable(){
@Override
public void run() {
FloatingActionButton fab = ref.get();
// You always have to check because it may disappear.
if(fab != null) {
fab.show();
}
}
}, 1000 * 60);
}
}
In this solution, the Runnable
and WeakReference
will stay in memory for the duration of the message, but the overhead is significantly less than a full Activity.
Upvotes: 2