Будет ли зацикливание работоспособной причины утечки памяти

Привет, у меня довольно большая утечка памяти в моем приложении, и я думаю, что это вызвано моими 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;, но у меня все еще происходит утечка всего Activity. Итак, мой вопрос: есть ли в этом базовом скелете что-то, что вызывает утечку памяти? Может быть, обработчик звонит сам себе?


person MikeT    schedule 01.08.2013    source источник
comment
какие вычисления вы делаете внутри обработчика. что может вызвать утечку памяти   -  person Raghunandan    schedule 01.08.2013
comment
вычисления не будут. Это бегун в целом, мне любопытно   -  person MikeT    schedule 01.08.2013
comment
из того, что я вижу, я не вижу ничего, что вызывает утечку памяти. android-developers.blogspot.in/2009/01/   -  person Raghunandan    schedule 01.08.2013
comment
как насчет того, что это нестатический внутренний класс?   -  person MikeT    schedule 01.08.2013
comment
всегда лучше сделать внутренний класс статическим. Вы используете контекст активности внутри внутреннего класса? если да, то в таком случае сделайте свой класс статическим и сохраните слабую ссылку на контекст активности.   -  person Raghunandan    schedule 01.08.2013
comment
да, я использую контекст деятельности. Когда я устанавливаю runanble на static, я не могу получить доступ к нестатическим переменным внешнего класса. Как мне обойти это?   -  person MikeT    schedule 01.08.2013
comment
конечно, есть работа, как я уже упоминал, с использованием слабой ссылки. не могли бы вы опубликовать весь код, пожалуйста, для получения дополнительной помощи   -  person Raghunandan    schedule 01.08.2013


Ответы (2)


Да, ваша реализация обязательно вызовет утечку памяти (я сам столкнулся с этим).

Проблема в том, что вы создали круговую ссылку. Вы объявили свой runnable как нестатический внутренний класс, что означает, что он будет автоматически поддерживать ссылку на действие. Сам runnable - это переменная-член вашей активности, которая замыкает круг. Сборщик мусора никогда не сможет освободить эти объекты, поскольку всегда будет живая ссылка.

Использование статического внутреннего класса со слабой ссылкой на действие - самый безопасный способ решить проблему. Здесь вы можете увидеть отличный пример кода. Если mainHandler является другим нестатическим внутренним классом, он создаст вторую циклическую ссылку по тем же причинам, поэтому вам придется проделать то же самое там.

Установка mainHandler.removeCallbacksAndMessages(null); и thread.randomAlienFire = null; также может работать, но вы должны быть очень осторожны при размещении этого кода. Возможно, в некоторых случаях код идет по другому пути, чем вы ожидаете, и пропускает эти вызовы? В этом сообщении блога описывается очень похожий опыт кого-то другого с этим подходом.

В моем случае я использовал runnable для последовательной анимации в ImageViews. чтобы избавиться от утечек памяти, я создал статический исполняемый класс, чтобы избежать циклической ссылки. Одного этого для меня было недостаточно, я также обнаружил, что drawable все еще сохраняет ссылку на мой фрагмент. вызов 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);
    }
}
person jzn    schedule 07.11.2013
comment
это правильный ответ? Я знаю, что есть утечка памяти, но думаю, что Java GC теперь может обрабатывать циклические ссылки. - person Charlesjean; 15.08.2014
comment
Как я вижу, у вас все еще есть утечка памяти с arrowAnimationRunnable , потому что вы создаете нестатический анонимный класс SafeRunnable, который содержит ссылку на родительский. Вы можете удалить SaveRunnable и использовать простой Runnable с myImageView.removeCallbacks(arrowAnimationRunnable); в onDestroyView. Он делает то же самое - person Nexen; 28.08.2015

По mainHandler.postDelayed(randomAlienFire, number ); вы ставите в очередь задачу, которая может иметь ссылку на память. Но деятельность может быть уничтожена до того, как будут выполнены фактические работы. Это вызывает у вас утечку памяти.

Чтобы избавиться от этой утечки, вы должны вызвать mainHandler.removeCallbacks(randomAlienFire); в соответствующем месте перед уничтожением активности. Например, если ваш runnable запускается из onStart (), вы должны вызвать mainHandler.removeCallbacks(randomAlienFire); в onStop ();

person Sazzad Hissain Khan    schedule 14.12.2013