0

嗨,我的应用程序中有相当大的内存泄漏,我认为这是由我的 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;但我仍然会泄漏整个活动。所以我的问题是,这个基本骨架中是否存在导致内存泄漏的东西?难道是处理程序正在调用自己的事实吗?

4

2 回答 2

5

是的,您的实现肯定会导致内存泄漏(我自己也遇到了这个问题)。

问题是您创建了循环引用。您已将 runnable 声明为非静态内部类,这意味着它将自动维护对活动的引用。runnable 本身是您的活动的成员变量,它关闭了圆圈。垃圾收集器将永远无法释放这些对象,因为总会有一个活动的引用。

使用对活动的弱引用的静态内部类是解决问题的最安全方法。你可以在这里看到一个很棒的代码示例。如果 mainHandler 是另一个非静态内部类,它将出于相同的原因创建第二个循环引用,因此您必须在那里做同样的事情。

设置 mainHandler.removeCallbacksAndMessages(null);thread.randomAlienFire = null; 也可以工作,但你必须非常小心你把代码放在哪里。也许代码在某些情况下采用了与您预期不同的路径并且错过了这些调用?这篇博文描述了其他人使用这种方法的非常相似的经历。

在我的例子中,我使用了一个 runnable 来对 ImageViews 上的动画进行排序。为了摆脱内存泄漏,我创建了一个静态可运行类来避免循环引用。仅此一点对我来说还不够,我还发现可绘制对象仍然保留对我的片段的引用。在我的片段中调用myImageView.removeCallbacksAndMessages(arrowAnimationRunnable);onDestroy() 终于解决了泄漏问题。这是我的解决方案:

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);
    }
}
于 2013-11-07T16:02:19.540 回答
3

你正在排队一个mainHandler.postDelayed(randomAlienFire, number ); 可能有内存引用的任务。但是活动可能会在实际工作完成之前被破坏。这会导致您的内存泄漏。

要消除这种泄漏,您必须mainHandler.removeCallbacks(randomAlienFire);在销毁活动之前调用适当的位置。例如,如果您的 runnable 从 onStart() 运行,则必须调用mainHandler.removeCallbacks(randomAlienFire);onStop();

于 2013-12-14T13:34:11.347 回答