众所周知的内存泄漏源是持有对活动的引用的AsyncTasks
,例如,当将任务声明为内部类时:
public class MyActivity extends Activity {
...
private class MyTask extends AsyncTask<Void, Void, Void> {
...
}
}
因为它们有一个关联的后台线程(或线程池),所以只要线程正在运行,即使活动被取消,对活动的引用也会保留。
现在(如果我错了,请纠正我),任务完成后,活动将再次可供收集。
我有一个活动,其中有:
- Runnables被定义为内部类,并通过处理程序发布在主线程上
- Timer任务定义为内部类
我可以通过取消计时器并在处理程序上调用removeCallbacks
来防止内存泄漏吗
示例:
public class MyActivity extends Activity {
Handler handler;
Timer timer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
timer = new Timer();
handler.postDelayed(new MyRunnable(), 10000);
timer.schedule(new MyTimerTask(), 1000);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
timer.cancel();
handler = timer = null;
}
private class MyTimerTask extends TimerTask {
...
}
private class MyRunnable implements Runnable {
...
}
}
我在我目前工作的项目中看到过这样一个活动,我正在考虑对其进行重构,使内部类成为静态的,并将WeakReferences添加到该活动中
问题是,内部类很方便,因为它们可以访问活动的私有变量和方法,将它们更改为static还会迫使我将活动类中这些成员的访问修饰符从private更改为package default,从而使它们可用于同一包中的任何其他类。这也打破了对称性,因为现在活动中有一些方法是包默认的,它们的对称对应方仍然是私有的(例如,我需要使活动中的stopFoo
方法对现在的静态类可用,那么我应该如何处理startFoo
,它不是从这些类调用的?)。那么活动中需要访问的变量呢?我现在应该提供一个getter吗
添加对活动的弱引用也在重构的静态类中引入了大量的null检查,因此只有在活动仍然存在的情况下,它们才起作用。除此之外,弱引用方法可能很难被公司中不熟练的程序员理解(此外,他们可能只是开始在任何地方使用它,而不是真正需要它)。
简而言之,重构后的活动看起来很难看,而上一个则很整洁。所以我想知道基于内部类的设计是否可以接受,因为我知道当活动结束时,我并不介意一个可运行的或计时器任务是否短时间运行
现在(如果我错了,请纠正我),任务完成后,活动将再次可用于收集
正确,假设没有其他东西对它有静态引用。
我可以通过取消计时器并在处理程序上调用removeCallbacks来防止内存泄漏吗?
这些步骤会有所帮助。此外,你无论如何都需要这样做——除非你停止Timer
,否则它不会神奇地停止运行
当活动超过时,我真的不介意可运行或计时器任务是否在短时间内运行
从内存管理的角度来看,您的论点是合理的。虽然"避免非静态内部类"是一个很好的高级设计标准,但它并不是绝对的。
然而,IMHO,您真正的问题不是内存管理,而是配置更改。当用户旋转屏幕、更改语言、将设备放入汽车底座等时,您的活动将被破坏并重新创建。然而,据推测,任务中的工作仍然是需要的,但他们会与错误的活动实例对话。使用保留的片段来管理这些任务有助于解决这个问题。