Поймать все возможные исключения Android глобально и перезагрузить приложение

Я знаю, что лучший способ предотвратить сбои системы - перехватывать все возможные исключения разными способами. Поэтому я использую блоки try try в любом месте моего кода. Однако, как вы знаете, иногда вы забываете тестировать некоторые сценарии, которые вызывают некоторые незапланированные исключения, и пользователь получает сообщение "К сожалению, приложение перестало работать...". Это плохо для любого приложения. К сожалению, люди, которые будут использовать мое приложение, не являются носителями английского языка, поэтому они также не поймут сообщение о сбое.

Поэтому я хочу знать, возможно ли перехватить все возможные исключения глобально (с помощью одного блока try catch в некоторых основных классах, а не во всех классах и методах!!!) и перезагрузить приложение автоматически и без каких-либо странных сообщений? Или, по крайней мере, возможно ли изменить сообщение о сбое?

Танки.

4 ответа

Решение

В вашем onCreate

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
                //Catch your exception
                // Without System.exit() this will not work.
                System.exit(2);
            }
        });

Поэтому я хочу знать, возможно ли перехватить все возможные исключения глобально... и автоматически перезагрузить приложение

НЕ ДЕЛАЙТЕ ЭТОГО. Если ваше приложение входит в состояние, в котором даже простой запуск вызывает сбой, вы создадите бесконечный цикл сбоя и повторного запуска, из которого пользователь не сможет выйти.

Сообщение об ошибке не странно; это системное сообщение, и оно переведено на все поддерживаемые языки на устройстве.

Единственное, что вам следует делать, - это использовать какую-то библиотеку отчетов о сбоях, чтобы сообщать вам о сбоях без необходимости что-либо делать, чтобы вы могли исправить свое приложение и отправить обновление. Вы можете установить пользовательский UncaughtExceptionHandler в вашем приложении onCreate(), но я бы ограничил это регистрацией данных и, возможно, подготовкой к отправке их вам в целях отладки, а затем переадресации обратного вызова на значение по умолчаниюUncaughtExceptionHandler, Это именно то, что делает библиотека отчетов о сбоях.

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

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

  1. Вызов метода может выдать проверенное исключение, и в этом случае компилятор заставляет вас окружить try-catch или повторно выбросить исключение; или же
  2. Вы хотите обработать определенные непроверенные (во время выполнения) исключения. Примером будет анализ пользовательского ввода с чем-то вроде Integer.parseInt(), ловя NumberFormatException и показывает сообщение пользователю, что его ввод недействителен.

Однако, как вы знаете, иногда вы забыли протестировать некоторые сценарии.

Затем улучшите кодирование и практику тестирования. Не используйте это как оправдание для принятия необдуманных действий.

К сожалению, люди, которые будут использовать мое приложение, не являются носителями английского языка, поэтому они также не поймут сообщение о сбое.

Какое сообщение о сбое? Если вы имеете в виду сообщение о сбое системы, оно должно быть на том языке, на котором они установили свое устройство. Если вы имеете в виду ваше аварийное сообщение (например, в logcat), они не должны этого делать. Они должны отправить вам сообщение о сбое, вы должны исправить приложение и распространить обновление. Не ожидайте, что конечный пользователь приложит какие-то усилия, чтобы определить причину сбоя вашего приложения. Лучше использовать какую-нибудь библиотеку отчетов о сбоях.

Там вы идете:

    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread paramThread, Throwable paramThrowable) {

        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(getActivity(),"Your message", Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();
        try
        {
            Thread.sleep(4000); // Let the Toast display before app will get shutdown
        }
        catch (InterruptedException e) {    }
        System.exit(2);
    }
});

Я нашел это полезным при отладке сбоев в моем приложении. Он не полагается на какой-либо существующий контекст или намерение; он настраивается с нуля.

В android.manifest добавьте ErrorReportActivity:

      <activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ch.calvert.gauges.ErrorReportActivity"
    android:theme="@style/Theme.AppCompat.Dialog">
</activity>

Создайте ErrorHandler.kt:

      import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.TextView
import java.io.PrintWriter
import java.io.StringWriter

private var oldHandler: Thread.UncaughtExceptionHandler? = null

class MyUncaughtExceptionHandler(context: Context) : Thread.UncaughtExceptionHandler {
    private val context: Context
    private val defaultHandler: Thread.UncaughtExceptionHandler?

    init {
        this.context = context.applicationContext
        defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
    }

    override fun uncaughtException(thread: Thread, exception: Throwable) {
        try {
            val sw = StringWriter()
            exception.printStackTrace(PrintWriter(sw))
            val intent = Intent(context, ErrorReportActivity::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            intent.putExtra("error_message", "Woops. An unexpected error occurred")
            intent.putExtra("stack_trace", sw.toString())

            // Start the new activity directly without using startActivity()
            if (context is Activity) {
                context.startActivity(intent)
            } else {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                context.startActivity(intent)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            System.exit(1)
        }
    }
}

class ErrorReportActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleUncaughtException()
    }

    private fun handleUncaughtException() {
        val error: String = intent.getStringExtra("error_message")!!
        val stackTrace:String = intent.getStringExtra("stack_trace")!!

        showDialogWithStackTrace(error, stackTrace)
    }

    private fun showDialogWithStackTrace(error: String, stackTrace: String) {
        val dialog = Dialog(this)
        dialog.setCancelable(true)

        val dialogLayout = LinearLayout(this)
        dialogLayout.orientation = LinearLayout.VERTICAL
        dialogLayout.setBackgroundColor(Color.WHITE)
        dialog.setContentView(dialogLayout)
        val errorTextView = createTextView(error,10f,Color.RED)
        val stackTraceTextView = createTextView(stackTrace, 8f, Color.BLACK)

        dialogLayout.addView(errorTextView)
        dialogLayout.addView(stackTraceTextView)

        dialog.show()
    }

    private fun createTextView(text: String, textSize: Float, textColor: Int): TextView {
        val textView = TextView(this)
        textView.text = text
        textView.textSize = textSize
        textView.setTextColor(textColor)
        textView.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT)
        return textView
    }
}

В своем основном действии создайте экземпляр обработчика:

          override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
Thread.setDefaultUncaughtExceptionHandler(MyUncaughtExceptionHandler(applicationContext));
    
        setContentView(R.layout.activity_main)
    }

Чуть позже в вашем приложении:

      override fun onDraw(canvas: Canvas) {
    throw Exception("SNAFU")
    ...

и вы увидите это:

Пожалуйста, избавьте меня от лекций о том, что это кощунство. Кому-то это может показаться полезным, но вы можете его игнорировать.

Да, и вдобавок к моим грехам, ChatGPT мне очень помог >;-).

Другие вопросы по тегам