Как обрабатывать исключения 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 для тех же самых неустранимых ошибок - т.е. отображать приглашение пользователю и изящно выходить из него. Зачем повторять этот вид обработки по всей кодовой базе снова и снова - его переделка - глобальный обработчик исключений для удовлетворения такого рода озабоченности имеет гораздо больший смысл!