Избежать сбоя потока обработчика в случае исключения во время выполнения?

У меня есть обработчик (поток), на котором я публикую Runnables. Если Runnable по ошибке выдает исключение RuntimeException, происходит сбой всего процесса приложения. По умолчанию это звучит разумно, но я хотел бы перехватывать такие исключения во время выполнения, регистрировать ошибку и продолжать работу. Однако у меня возникают трудности с выполнением этой работы. Я думал о следующих вариантах:

  1. Подкласс Looper и переопределение loop(), которое содержит код, вызывающий метод run() опубликованных Runnables. Это не сработало, так как Looper является окончательным.
  2. Подкласс HandlerThread и переопределить метод запуска, который вызывает класс Looper.loop(). В случае исключения во время выполнения снова вызовите super.run() и так далее. Это не сработало, поскольку «в каждом потоке может быть создан только один Looper».
  3. В каждый исполняемый класс включите try-catch для исключений времени выполнения. Это будет работать, но довольно раздражает, так как у меня довольно много разных классов Runnable. Кроме того, не защитит поток обработчика, если исполняемый файл по ошибке забудет включить try-catch.

В идеальном мире у Looper был бы метод-член с именем registerExceptionHandler(ExceptionHandler), а затем в случае исключения вызывался бы ExceptionHandler.

У кого-нибудь есть лучшее предложение?


person TommyTh    schedule 18.02.2014    source источник
comment
поместите код runnable в блок try-catch и перехватите исключение. Таким образом, runnable не будет генерировать перехваченные исключения. Вы можете управлять исключением в блоке catch в соответствии с вашими потребностями.   -  person Gaurav Gupta    schedule 18.02.2014
comment
Правильно, я думаю, что это то, что я называю вариантом 3, и я рассматриваю это как обходной путь. Проблема в том, что у меня есть несколько классов Runnable, и я хотел бы избежать добавления такого кода в каждый из них.   -  person TommyTh    schedule 18.02.2014


Ответы (3)


попробуйте этот пользовательский обработчик:

class H extends Handler {
    public H(Looper looper) {
        super(looper);
    }

    @Override
    public void dispatchMessage(Message msg) {
        // catch any Exception
        try {
            super.dispatchMessage(msg);
        } catch (Exception e) {
            Log.d(TAG, "dispatchMessage " + e.getMessage());
        }
    }
}

код тестирования:

HandlerThread ht = new HandlerThread("hthread");
ht.start();
Handler h = new H(ht.getLooper());

Runnable r = new Runnable() {
    @Override
    public void run() {
        throw new RuntimeException("testing exception");
    }
};
h.post(r);
h.post(r);
person pskink    schedule 18.02.2014
comment
Да, именно так вы настраиваете и запускаете HandlerThread. Если вы сделаете что-то вроде ht.post(new RuntimeGeneratingRunnable), процесс приложения рухнет. Есть идеи, как этого избежать? - person TommyTh; 18.02.2014
comment
не используйте Runnables, используйте sendMessage и Handler.Callback - person pskink; 18.02.2014
comment
Правильно, это сработает. Мне пришлось бы перейти на использование сообщений вместо Runnables, поэтому я думаю, что предпочел бы добавить try-catches ко всем Runnables... Если ничего лучшего не появится. - person TommyTh; 18.02.2014
comment
или вы также можете использовать Runnables, но вам нужен собственный обработчик с переопределенным методом dispatchMessage: try { super. так далее - person pskink; 18.02.2014
comment
Пожалуйста, обновите свой ответ, чтобы он соответствовал требованиям создателя вопроса. :) - person CSchulz; 18.02.2014
comment
pskink: Я искал эквивалент dispatchMessage() для runnables, но не могу его найти... На самом деле я не вижу никакого способа повлиять на то, как обрабатываются Runnables. Я также рассматривал обработчик подклассов, но не вижу, как это поможет. Может я неправильно понимаю, что вы предлагаете? - person TommyTh; 18.02.2014
comment
@TommyTh Runnables отправляются в dispatchMesdage, попробуйте мой код - person pskink; 18.02.2014
comment
Ты абсолютно прав. Runnables также маршрутизируются через dispatchMessage(). Должно быть, какое-то специальное сообщение. Спасибо, это было именно то, что мне было нужно! - person TommyTh; 18.02.2014
comment
@TommyTh еще одно замечание: если у вас есть десятки Runnables, я бы предложил переопределить handlwMessage для повышения производительности и использования Messages, поскольку каждый Runnable — это отдельный класс, который должен загружаться vm, я видел в официальных источниках, что Google предпочитает Messages Runnables - person pskink; 18.02.2014

В идеальном мире DefaultExceptionHandler работает для меня как шарм на протяжении всего приложения. Там, где он вызывает необработанное исключение, он срабатывает.

Библиотека с именем Android-Remote-StackTrace. Вам просто нужно зарегистрировать трансляцию этого класса DefaultHandlerException в вашем классе Application.

Фрагмент:

 public class Portfolio extends Application {
 static Portfolio portfolio;
 public void onCreate() {
    super.onCreate();
    portfolio = this;
    ExceptionHandler.register(portfolio);// registering for unhandled Exceptions
   }
 }

Вы можете выполнять с ними различные задачи, такие как загрузка трассировки стека журнала на ваш сервер или отправка писем о сбое приложения в виде отчета о сбое с необработанной трассировкой стека.

person Vikalp Patel    schedule 18.02.2014
comment
Не совсем то, что я просил, но определенно кажется полезным. Моя непосредственная мысль состоит в том, что уже слишком поздно восстанавливать поток обработчика, но я не уверен. Я проведу расследование и дам вам знать. Спасибо за ответ! - person TommyTh; 18.02.2014
comment
Я посмотрел на это. Я не знал о существовании DefaultExceptionHandler; безусловно интересно и стоит прочтения. В данном конкретном случае это будет означать, что все ожидающие исполнения в очереди будут потеряны, а поток обработчика придется перезапустить и т. д. Так что, к сожалению, это не совсем подходит :-(. - person TommyTh; 18.02.2014

Самый простой способ, кажется, получить из Runnable:

public abstract class RunnableBase implements Runnable
{
    public final void run()
    {
        try {
            runInternal();
        }
        catch(Exception e) {
            // handle the exception
        }
    }

    // to be implemented by concrete subclasses
    protected abstract void runInternal();
}
person jlothian    schedule 10.06.2014