Reputation: 255
I am using a timer that is canceled and restarted on a listener event. It all works fine except that the the timer thread leaks the whole outer class.
My timer implementation is as follows:
if(timer != null) {
timer.cancel();
timer = null;
timer = new Timer();
}
timer.schedule(new TimerTask() { // Thread leaks!!!!
@Override
public void run() {
mCallback.onHeaderMoving(newToolbarTranslationY );
}
} , 150);
I used MAT Analyser to track down the problem and ended up there. I also commented out the line with the callback but the thread still leaks so it is defenetly the timer itself. However I don't really understand what is the problem with that code.
As far as I understand from my research the problem is that the anonymous inner class (new Timertask()) holds a reference to the outer class and therefore can leak the whole context. But I still don't understand why the timer and also the reference to the context is not garbage collected after the thread runs out (after 150 ms +).
Is the context in this case somehow still not released even after the thread finished?
And finally how do I solve this leak? I set the timer to null but that didn't solved my problem.
Edit
private OnHeaderMovingCallBack mCallback;
private Timer timer = new Timer();
//... some other parameters
public ScrollingToolbarManager(View toolbar , View pagerStrip , AbsListView listView , OnHeaderMovingCallBack headerMovingCallBack){
this.toolbar = toolbar;
this.pagerStrip = pagerStrip;
this.listView = listView;
mCallback = headerMovingCallBack;
changeStartValues();
}
public static interface OnHeaderMovingCallBack{
public void onHeaderMoving(int translationY);
}
public void moveHeader(){
//... some calculations
//timer implementation from above
}
moveHeader() is called on a scroll event of a listview
Upvotes: 4
Views: 10137
Reputation: 39
I have the same problem with you. I found that when I define Timer
as global var and don't set it to null
when the activity finished, it always leads memory leak.
And when I define Timer
as local var or set it to null
, the problem gone.But I don't understand why. If you had solved it, please tell me your solution, thanks!
public class TestActivity extends AppCompatActivity {
private Timer mTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0);
}
@Override
protected void onDestroy() {
super.onDestroy();
mTimer = null;
}
}
Upvotes: 2
Reputation: 17794
If you think that the problem is that the anonymous inner class holds a reference to the outer class, then simply use a static named inner class - this will hold no reference. Put something like this inside your class:
static class MyTimerTask extends TimerTask {
private OnHeaderMovingCallBack mCallback;
int newToolbarTranslationY;
public MyTimerTask(OnHeaderMovingCallBack mCallback, int newToolbarTranslationY) {
this.mCallback = mCallback;
this.newToolbarTranslationY = newToolbarTranslationY;
}
@Override
public void run() {
mCallback.onHeaderMoving(newToolbarTranslationY);
}
}
Upvotes: 3