Использование глобальной обработки исключений на 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)

Так что это бесконечно растущая цепь, вызывающая две проблемы:

  1. Указанный пользовательский код (в моем случае запись в файл журнала) будет вызываться несколько раз, что не является желаемым действием.
  2. Ссылка на 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()
    }
Другие вопросы по тегам