Использование глобальной обработки исключений на Android
Есть ли пример кода или учебник о том, как использовать Thread.setDefaultUncaughtExceptionHandler
метод? По сути, я пытаюсь отобразить в моем приложении настраиваемое диалоговое окно с предупреждением, когда возникает исключение. Можно ли сделать это? Я знаю, что немного сложно отобразить что-то на экране, если исключение выдается в потоке пользовательского интерфейса, но я не знаю, как это обойти.
7 ответов
Основной пример для тех, кто приходит на эту страницу с решением:)
public class ChildActivity extends BaseActivity {
@SuppressWarnings("unused")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int a=1/0;
}
}
Класс для обработки ошибок:
public class BaseActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
Log.e("Alert","Lets See if it Works !!!");
}
});
}
}
Вот вариант ответа Code_Life со следующими улучшениями:
- Не приводит к зависанию приложения / службы после обработки ошибок
- Позволяет Android делать свою обычную обработку ошибок после вашего собственного
Код:
public class BaseActivity extends Activity {
@Override
public void onCreate() {
super.onCreate();
final Thread.UncaughtExceptionHandler oldHandler =
Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(
Thread paramThread,
Throwable paramThrowable
) {
//Do your own error handling here
if (oldHandler != null)
oldHandler.uncaughtException(
paramThread,
paramThrowable
); //Delegates to Android's error handling
else
System.exit(2); //Prevents the service/app from freezing
}
});
}
}
Для тех, кто просто хочет видеть детали исключения, когда ваше приложение вылетает на устройстве (в отладочной конфигурации). Это класс приложения:
private Thread.UncaughtExceptionHandler oldHandler;
@Override
public void onCreate() {
super.onCreate();
if (!BuildConfig.DEBUG)
return;
oldHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
try {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, sw.toString());
intent.setType("text/plain");
startActivity(intent);
} catch(Exception ex) {
ex.printStackTrace();
} finally {
if (oldHandler != null)
oldHandler.uncaughtException(t, e);
else
System.exit(1);
}
});
}
Он использует внешнее приложение, поскольку ваш поток пользовательского интерфейса может больше не работать.
Имейте в виду, что The RuntimePermission("setDefaultUncaughtExceptionHandler")
проверяется перед установкой обработчика и убедитесь, что впоследствии вы вызвали остановку процесса, выдав необработанное исключение, так как все может быть в неопределенном состоянии.
Ничего не отображать, действительно, поток пользовательского интерфейса мог быть тем, который потерпел крах, вместо этого запишите журнал и / или отправьте подробности на сервер. Возможно, вы захотите проверить этот вопрос и его ответы.
Я просто хотел показать свой опыт до сих пор. Я использую решение, предложенное /questions/31818986/ispolzovanie-globalnoj-obrabotki-isklyuchenij-na-android/31819005#31819005 чтобы сбросить исключение в мой файл журнала перед передачей управления обработчику исключений по умолчанию.
Тем не менее, моя структура выглядит следующим образом:
BaseActivity
|
_______________________
| | |
Activity A Activity B Activity C
final Thread.UncaughtExceptionHandler defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable e) {
handleUncaughtException(thread, e, defaultEH);
}
});
где handleUncaughtException(thread, e, defaultEH);
записывает в журнал и передает вызов исходному UncaughtExceptionHandler.
Итак, что случилось с использованием этого кода было следующее:
- Деятельность А создается
- Новый обработчик исключений по умолчанию (DEH) теперь мой обработчик журнала + старый DEH
- Деятельность B создается
- Новый DEH теперь мой обработчик журналов + старый DEH (обработчик журналов + оригинальный DEH)
- Деятельность C создается
- Новый DEH теперь мой обработчик журнала + старый DEH (обработчик журнала + обработчик журнала + оригинальный DEH)
Так что это бесконечно растущая цепь, вызывающая две проблемы:
- Указанный пользовательский код (в моем случае запись в файл журнала) будет вызываться несколько раз, что не является желаемым действием.
- Ссылка на defaultEh сохраняется в куче даже после завершения действия, поскольку она используется цепочкой ссылок, поэтому худшее, что может произойти, - это исключение нехватки памяти.
Поэтому я добавил еще одну вещь, чтобы наконец сделать эту работу без проблем:
private static boolean customExceptionHandlerAttached = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!customExceptionHandlerAttached) {
final Thread.UncaughtExceptionHandler defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable e) {
handleUncaughtException(thread, e, defaultEH);
}
});
customExceptionHandlerAttached = true;
}
}
С этим решением мы можем убедиться, что:
- добавить пользовательский обработчик исключений для нашего желаемого действия
- убедитесь, что это действие запускается только один раз
- позволяя сборщику мусора полностью уничтожить нашу деятельность, вызывая finish()
Если вы хотите использовать эту библиотеку
https://github.com/selimtoksal/Android-Caught-Global-Exception-Library
создать свой TransferObject не все в вашей деятельности, просто использовать в базовой деятельности
Вам необходимо добавить следующий код в класс приложения
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler { _, e -> handleUncaughtException(e) }
}
private fun handleUncaughtException(e: Throwable) {
if (isUIThread()) invokeLogActivity(e)
else Handler(Looper.getMainLooper()).post { invokeLogActivity(e) }
}
private fun isUIThread(): Boolean {
return Looper.getMainLooper().thread === Thread.currentThread()
}
private fun invokeLogActivity(e: Throwable) {
val intent = Intent(currentActivity(), ErrorActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.putExtra("stackTrace", getStackTraceAsString(e))
startActivity(intent)
exitProcess(1)
}
private fun getStackTraceAsString(throwable: Throwable): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
throwable.printStackTrace(pw)
return sw.toString()
}