大家好,我的应用程序中存在相当大的内存泄漏,我认为这是由我的Runnables造成的。 这是我使用的Runnables骨架的一个例子:
private Runnable randomAlienFire = new Runnable() {
public void run() {
/*A Bunch
of computations
*/
mainHandler.removeCallbacks(randomAlienFire);
mainHandler.postDelayed(randomAlienFire, number );
}
当我切换活动时,我会调用mainHandler.removeCallbacksAndMessages(null);
和thread.randomAlienFire = null;
但我仍在泄漏整个活动。所以我的问题是,这个基本骨架中是否存在导致内存泄漏的问题?可能是处理程序正在调用它自己的事实吗?
答案 0 :(得分:5)
是的,你的实现肯定会导致内存泄漏(我自己就碰到了这个)。
问题是您已创建循环引用。您已将runnable声明为非静态内部类,这意味着它将自动维护对活动的引用。 runnable本身是您活动的成员变量,它会关闭圆圈。垃圾收集器将永远无法释放这些对象,因为总会有生命参考。
使用具有对活动的弱引用的静态内部类是解决问题的最安全方法。 You can see a great code example here.如果mainHandler是另一个非静态内部类,它会出于同样的原因创建第二个循环引用,所以你必须在那里做同样的事情。
设置mainHandler.removeCallbacksAndMessages(null);
和thread.randomAlienFire = null;
也可以有效,但在放置代码时必须非常小心。在某些情况下,代码可能会采用与您预期不同的路径并错过这些调用? This blog post describes someone else's very similar experience with that approach.
就我而言,我正在使用runnable对ImageViews上的动画进行排序。为了摆脱内存泄漏,我创建了一个静态的runnable类来避免循环引用。仅此一点对我来说还不够,我还发现drawable仍然保留了对我片段的引用。在片段中的onDestroy()中调用myImageView.removeCallbacksAndMessages(arrowAnimationRunnable);
终于解决了泄漏问题。这是我的解决方案:
public class MyFragment extends SherlockFragment {
public static class SafeRunnable implements Runnable {
private final WeakReference<MyFragment> parentReference;
public SafeRunnable(MyFragment parent) {
parentReference = new WeakReference<MyFragment>(parent);
}
@Override
public void run() {
if (parentReference != null) {
final MyFragment parent = parentReference.get();
if (parent != null) {
runWithParent(parent);
}
}
}
public void runWithParent(MyFragment parent) {
}
}
// This anonymous instance of the new runnable class does not retain a
reference to the fragment
private Runnable arrowAnimationRunnable = new SafeRunnable(this) {
@Override
public void runWithParent(MyFragment parent) {
// ... animation code
// repeat the animation in 1 second
parent.myImageView.postDelayed(this, 1000);
}
};
private ImageView myImageView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_layout, container, false);
// find the image view and kick off the animation after 1 second
myImageView = (ImageView) view.findViewById(R.id.iv_arrow);
myImageView.postDelayed(arrowAnimationRunnable, 1000);
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
// It's necessary to remove the callbacks here, otherwise a message will
// be sitting in the queue and will outlive the fragment. Because a
// reference in that message will still be pointing to the fragment, the
// fragment (and everything else) will not be garbage collected
myImageView.removeCallbacks(arrowAnimationRunnable);
}
}
答案 1 :(得分:3)
按mainHandler.postDelayed(randomAlienFire, number );
你正在排队一个可能有内存参考的任务。但是在实际工作完成之前,活动可能会被破坏。这会导致内存泄漏。
要消除此泄漏,您必须在销毁活动之前在适当的位置呼叫mainHandler.removeCallbacks(randomAlienFire);
。例如,如果runnable从onStart()运行,则必须在onStop()中调用mainHandler.removeCallbacks(randomAlienFire);
;