Как обрабатывать исключения MontoDroid во всех случаях и предотвращать крах приложения

Я пытаюсь реализовать правильную обработку исключений в моем приложении для монодидов, которое написано с помощью плагина Xamarin.Android для Visual Studio.

Я пытаюсь обрабатывать 2 типа исключений:

  • на переднем плане (UI)
  • в фоновом режиме (threadpool)

В обоих случаях в глобальном обработчике я хочу:

  • Ведение журнала - (отправка события аналитики)
  • Уведомление пользователя - (предупреждение)

После определенного расследования я нашел несколько ответов здесь, здесь и здесь, но ничего, кроме AndroidEnvironment.UnhandledExceptionRaiser и AppDomain.UnhandledException, было предложено, и оно не работает во всех случаях.

Я создал короткий образец, где я пытаюсь использовать оба обработчика:

AppDomain.CurrentDomain.UnhandledException += (s,e)=>
{
    System.Diagnostics.Debug.WriteLine("AppDomain.CurrentDomain.UnhandledException: {0}. IsTerminating: {1}", e.ExceptionObject, e.IsTerminating);
};

AndroidEnvironment.UnhandledExceptionRaiser += (s, e) =>
{
    System.Diagnostics.Debug.WriteLine("AndroidEnvironment.UnhandledExceptionRaiser: {0}. IsTerminating: {1}", e.Exception, e.Handled);
    e.Handled = true;
};

И затем при нажатии кнопки я добавил следующий код для повышения обоих типов исключений:

//foreground exception
throw new NullReferenceException("test nre from ui thread.");
//background exception
ThreadPool.QueueUserWorkItem(unused =>
{
    throw new NullReferenceException("test nre from back thread.");
});

В результате у меня другое поведение для обоих типов исключений:

  • на первый план:
    • оба обработчика подняты
    • невозможно запретить приложение разбился - он будет разбит любым способом (e.Handled = true просто игнорируется)
  • фон:
    • создается только второй обработчик
    • приложение не сбой

В моем случае я не мог обернуть каждое действие пользователя в try-catch, особенно фоновые задачи. У меня есть бизнес-логин, который следует прервать в случае ошибки, и это именно то, чего я ожидаю от времени выполнения. В то же время я хочу обработать это исключение на верхнем уровне в одном месте, зарегистрировать их (на основе моих бизнес-правил) и продолжить выполнение приложения.

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

Здесь вы можете найти полный пример кода: https://dl.dropboxusercontent.com/u/19503836/UnhandledException.zip

Спасибо за ваш совет. Любая помощь оценивается. ТИА!

Ответ 1

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

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

Как говорится в комментарии к вашему вопросу, это плохая идея для обработки таких исключений.

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

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

Документация Microsoft для AppDomain.CurrentDomain.UnhandledException содержит дополнительную информацию об этом:

http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

Ответ 2

@Daveoc64 - Я не согласен с вами (и другими), которые подчеркнули, что глобальная обработка исключений, не связанных с ошибками, является плохой идеей - я бы сказал, что это необходимо для приложений пользовательского интерфейса. Это не отменяет правильную обработку исключений на уровне метода (т.е. Где вы можете обрабатывать и восстанавливать определенные исключения, в которых контекст очень важен, как вы подчеркивали), - но он использовался в дополнение к нему, поскольку иногда, когда возникает исключение, нет восстановления для приложения. Поэтому помещать попытку/улов в код, чтобы поймать исключение, из которого вы не можете восстановить, бессмысленно - как и все, что вы помещаете внутри этого блока catch, вам придется копировать и помещать внутри всех остальных блоков catch для тех же самых неустранимых ошибок - т.е. отображать приглашение пользователю и изящно выходить из него. Зачем повторять этот вид обработки по всей кодовой базе снова и снова - его переделка - глобальный обработчик исключений для удовлетворения такого рода озабоченности имеет гораздо больший смысл!