Утечка: Таймер и TextWatcher

Я работаю над editText и recyclerView. Мой recyclerView обновляется, когда я пишу письма в своем EditText.

Я помещаю таймер в свой textWatcher, чтобы избежать отправки запросов каждый раз, когда пользователь пишет письмо.

searchDestinationEt.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
            //There is nothing to do here
        }

        @Override
        public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
            if (timer != null) {
                timer.cancel();
            }
        }

        @Override
        public void afterTextChanged(final Editable s) {

            timer = new Timer();

            //we schedule this in order to avoid sending useless request.
            //We wait the user is finishing writing before sending requests
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            actionsListener.onDestinationSearch(s.toString());
                        }
                    });
                }
            }, DELAY_SEND_REQUEST);
        }
    });

Это работает хорошо, но Leakcanary говорит, что у меня есть утечка в этой части кода. Любая идея ?


person Bob    schedule 20.06.2016    source источник
comment
mopri.de/2010 /timertask-bad-do-it-android-way-use-a-handler   -  person oiZo    schedule 20.06.2016
comment
@oiZo Спасибо, я попробую. Но это не объясняет, почему там есть утечка :/.   -  person Bob    schedule 20.06.2016


Ответы (2)


Извините за опоздание с ответом, но вы пытались разделить textwatcher следующим образом?: TextWatcher для более одного EditText

person Community    schedule 04.08.2017

  1. Почему вы используете Timer и TimerTask для отложенных, а не повторяющихся действий? Самый простой и распространенный способ — использовать обычный Handler с postDelayed():

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
           //do somthing here
        }
    }, DELAY_SEND_REQUEST);
    
  2. Утечка происходит из-за того, что вы запускаете поток, который имеет ссылку на ваш контекст (фрагмент или активность). И пока ваша нить не будет завершена, она не будет удалена сборщиком мусора.

Это означает, например, что если пользователь что-то вводит, а вы ждете, пока вы начнете запрашивать, а в это время пользователь поворачивает телефон и происходит смена ориентации — ваша активность/фрагмент будет воссоздана, но старая (которая запустила поток и должен использоваться, когда поток выполнен) не пропал и все еще присутствует в памяти.

  1. Зачем делать запрос в потоке пользовательского интерфейса? Он блокирует пользовательский интерфейс, вы знаете это, верно? Я предполагаю, что AsyncTask может подойти лучше.

Что вы должны сделать? Замените Timer на Handler и выполняйте запросы в рабочем потоке. Что касается утечки, у вас есть 2 варианта:

а) ничего не делать, так как время, в течение которого ваша активность/фрагмент будет сохранена, очень мало, и она будет проверена после выполнения запроса. (не рекомендуется)

б) Используйте AsyncTask и в конструкторе AsyncTask передайте контекст (ваш слушатель) и сохраните его как объект слабой ссылки, например:

private static class SomeWorkTask extends AsyncTask<Void,Void,Void>{

    private WeakReference<ActionsListenerWithContext> weakListener;

    public SomeWorkTask(ActionsListenerWithContext listener){
        this.weakListener = new WeakReference<>(listener);
    }

    @Override
    protected Void doInBackground(Void... voids) {
        //do some work here
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        if(weakListener.get() != null){
            weakListener.get().callYourCallbacks();
        }
    }
}

а потом ты называешь это

 new SomeWorkTask(listener).execute();

Использование оболочки WeakReference является общепринятой и рекомендуемой практикой.

person Kirill Karmazin    schedule 15.07.2019