Поймать все возможные исключения 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 только там, где
- Вызов метода может выдать проверенное исключение, и в этом случае компилятор заставляет вас окружить try-catch или повторно выбросить исключение; или же
- Вы хотите обработать определенные непроверенные (во время выполнения) исключения. Примером будет анализ пользовательского ввода с чем-то вроде
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 мне очень помог >;-).