Должен ли я вручную закрывать HandlerThreads, созданные моим приложением, при уничтожении активности?

Мое приложение состоит из одного файла Activity. В этом упражнении я создаю несколько HandlerThread, которые запускаются в цикле для выполнения операций блокировки сокетов.

В настоящее время я отправляю сообщение о выходе всем из этих HandlerThread во время моего Activity.onDestroy().

Иногда, когда я открываю свое приложение, закрываю его и перезапускаю, оно падает (много раз из-за отправки сообщения в поток обработчика, который не запущен).

Мой вопрос: Как правильно закрыть HandlerThread, когда я закрываю свое приложение? (Обратите внимание, что эти потоки могут блокироваться в операции сокета).

EDIT: дополнительная информация: у меня есть пул потоков обработчиков, который инициируется в onCreate (нет проблем, когда я запускаю свое приложение в первый раз).

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

 if (shouldRun) {
//body
} 
else { 
 close();
}

утверждение.

метод close удаляет все ожидающие сообщения и исполняемые файлы и отправляет обработчику сообщение, которое заставит его вызвать его looper.quit(). Таким образом, если текущий поток обработчика заблокирован операцией ввода-вывода, только после того, как он закончит ее, он выйдет ().


person Daniel L.    schedule 18.02.2013    source источник
comment
Пожалуйста, проанализируйте причину сбоя. Возможно, истинная причина в том, что ваши объекты HandlerThread пытаются опубликовать что-то в Activity, которое больше не существует. Вам нужно сообщить своим потокам, которые все еще работают, что они потеряли свой контекст.   -  person class stacker    schedule 18.02.2013


Ответы (5)


  • У вас должно быть какое-то несоответствие, иначе ваше приложение не вылетит. Вы уверены, что причиной действительно является HandlerThread, который не запущен? Разве вы не создаете объекты HandlerThread при создании вашей деятельности?
  • Если ваши HandlerThreads ожидают операций ввода-вывода, я бы попытался их прервать. Простое удаление обратных вызовов и сообщений и просьба выйти из Лупера, даже отправка сообщений о завершении обработчику ничего не даст. Объект HandlerThread все еще будет существовать до тех пор, пока Android не завершит процесс (что может произойти или не произойти). Это означает, что «ваше приложение будет собирать зомби-объекты HandlerThread», которые, вероятно, недоступны. Если, конечно, вы не можете отправить этим HandlerThreads сообщение о завершении, которое приходит на канал, который они блокируют.
  • Было бы намного лучше повторно использовать объекты HandlerThread. Служба может быть подходящей моделью для этого.
  • Кроме того, если потоки пережили вашу активность, вам нужно сообщить им, что их коммуникационный пир (ваша активность) исчез. В противном случае они могут относиться к чему-то, чего уже нет.
person class stacker    schedule 18.02.2013
comment
Пожалуйста, опубликуйте исключение с трассировкой стека. Кроме того, основной вопрос заключается в том, поддерживают ли ваши объекты HandlerThread ссылки на вашу активность. - person class stacker; 18.02.2013

Да, было бы неплохо закрыть его. Также не забудьте удалить свои обратные вызовы.

@Override
public void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
    handler.getLooper().quit();
}
person Andy McSherry    schedule 18.02.2013
comment
Но затем я получаю эти сбои при перезапуске моего приложения. Уже пробовал расширенный ответ.. - person Daniel L.; 18.02.2013
comment
Вы создаете новый обработчик/поток при перезапуске и снова вызываете Looper.prepare()/Looper.loop()? - person Andy McSherry; 18.02.2013
comment
Насколько я понимаю, у @Daniel есть объекты HandlerThread, которые ждут блокировки операций ввода-вывода. Просьба Looper выйти() не будет иметь немедленного эффекта. - person class stacker; 18.02.2013

    /**
 * Ask the currently running looper to quit.  If the thread has not
 * been started or has finished (that is if {@link #getLooper} returns
 * null), then false is returned.  Otherwise the looper is asked to
 * quit and true is returned.
 */
public boolean quit() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quit();
        return true;
    }
    return false;
}

Выше показан метод выхода из исходного кода HandlerThread.java, просто вызовите его напрямую.

Почему должен звонить бросить? Ниже приведен метод запуска исходного кода HandlerThread.java.

    public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();//always loop except for a Message with null target

    mTid = -1;
}

Ответ: «цикл - это метод while (true)», он будет возвращаться до тех пор, пока не получит сообщение с нулевой целью.

person Ql An    schedule 02.02.2015

Лучше всего было бы удалить обратные вызовы для ваших обработчиков в ваших действиях onDestroy(). Смотрите этот ответ для получения дополнительной информации:

https://stackoverflow.com/a/5038542/1369222

person Anup Cowkur    schedule 18.02.2013

HandlerThread останавливается при выходе из Looper. HandlerThread.getLooper().quit(), когда вы прекращаете свою деятельность. (см. http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/app/IntentService.java#IntentService.ServiceHandler.%3Cinit%3E%28android.os.Looper%29 для хорошего примера правильного использования HandlerThread)

person njzk2    schedule 18.02.2013
comment
Насколько я понимаю, у @Daniel есть объекты HandlerThread, которые ждут блокировки операций ввода-вывода. Просьба Looper выйти() не будет иметь немедленного эффекта. - person class stacker; 18.02.2013
comment
возможно, да. В этом случае handlerThread.interrupt() должен остановить эти операции (но это не относится к HandlerThread). - person njzk2; 18.02.2013