Mostrar un diálogo en `Thread.setDefaultUncaughtExceptionHandler`

Cuando mi aplicación androide lance una excepción, quiero mostrar un diálogo personalizado para decirle al usuario que hay algo mal sucedido, así que utilizo Thread.setDefaultUncaughtExceptionHandler para establecer un manejador de excepciones global:

 public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, final Throwable ex) { AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext()); builder.setTitle("There is something wrong") .setMessage("Application will exit:" + ex.toString()) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // throw it again throw (RuntimeException) ex; } }) .show(); } }); } } 

Pero he encontrado que hay alguna excepción lanzada, el AlertDialog no se mostrará, en su lugar, los bloques de aplicación y después de un tiempo, se mostrará un diálogo del sistema:

 X app is not responding. Would you like to close it? Wait | OK 

¿Qué debería hacer ahora?


ACTUALIZAR

El registro:

 11-16 12:54:16.017: WARN/WindowManager(90): Attempted to add window with non-application token WindowToken{b38bb6a8 token=null}. Aborting. 

Parece que el error viene de new AlertDialog.Builder(getApplicationContext());

Pero esto es un manejador de excepciones en la subclase de Application , ¿cómo puedo establecer una instancia de actividad para él?

No se puede hacer ninguna operación de interfaz de usuario desde aquí. Simplemente inicie otra actividad / pantalla de bienvenida. Pase un intento extra para denotar crash y mostrar diálogo en esa actividad.

  /* * (non-Javadoc) * * @see * java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java. * lang.Thread, java.lang.Throwable) */ @Override public void uncaughtException(Thread t, final Throwable e) { StackTraceElement[] arr = e.getStackTrace(); final StringBuffer report = new StringBuffer(e.toString()); final String lineSeperator = "-------------------------------\n\n"; report.append(DOUBLE_LINE_SEP); report.append("--------- Stack trace ---------\n\n"); for (int i = 0; i < arr.length; i++) { report.append( " "); report.append(arr[i].toString()); report.append(SINGLE_LINE_SEP); } report.append(lineSeperator); // If the exception was thrown in a background thread inside // AsyncTask, then the actual exception can be found with getCause report.append("--------- Cause ---------\n\n"); Throwable cause = e.getCause(); if (cause != null) { report.append(cause.toString()); report.append(DOUBLE_LINE_SEP); arr = cause.getStackTrace(); for (int i = 0; i < arr.length; i++) { report.append(" "); report.append(arr[i].toString()); report.append(SINGLE_LINE_SEP); } } // Getting the Device brand,model and sdk verion details. report.append(lineSeperator); report.append("--------- Device ---------\n\n"); report.append("Brand: "); report.append(Build.BRAND); report.append(SINGLE_LINE_SEP); report.append("Device: "); report.append(Build.DEVICE); report.append(SINGLE_LINE_SEP); report.append("Model: "); report.append(Build.MODEL); report.append(SINGLE_LINE_SEP); report.append("Id: "); report.append(Build.ID); report.append(SINGLE_LINE_SEP); report.append("Product: "); report.append(Build.PRODUCT); report.append(SINGLE_LINE_SEP); report.append(lineSeperator); report.append("--------- Firmware ---------\n\n"); report.append("SDK: "); report.append(Build.VERSION.SDK); report.append(SINGLE_LINE_SEP); report.append("Release: "); report.append(Build.VERSION.RELEASE); report.append(SINGLE_LINE_SEP); report.append("Incremental: "); report.append(Build.VERSION.INCREMENTAL); report.append(SINGLE_LINE_SEP); report.append(lineSeperator); Log.e("Report ::", report.toString()); Intent crashedIntent = new Intent(BaseActivity.this, SplashActivity.class); crashedIntent.putExtra(EXTRA_CRASHED_FLAG, "Unexpected Error occurred."); crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(crashedIntent); System.exit(0); // If you don't kill the VM here the app goes into limbo } 

Ver también:

Android UncaughtExceptionHandler que instancia un AlertDialog se rompe

Tostadas no aparecen en UnCaughtExceptionHandler

Cómo iniciar la actividad de UncaughtExceptionHandler si este es el hilo principal se estrelló?

Cómo lo hago:

Tengo un BaseActivity que extiende la actividad, y en el onCreate de la actividad fijé el UncaughtExceptionHandler. Todas mis actividades amplían la BaseActivity en lugar de Actividad.

Llaves

  1. No puede establecer el manejador de excepciones en Application.onCreate , sino que debe crear una BaseActivity y establecerla en el método onCreate de la misma.
  2. Después de iniciar SplashActivity, debemos llamar a System.exit(0)
  3. No podemos mantener la instancia de error para compartirla con SplashActivity , ya que será destruida, en su lugar, podemos pasar algún mensaje de error o persistir en el archivo.

Parece que la solución proporcionada no funciona (al menos para Android 4.0 y superiores). Para cualquier persona que pueda estar interesado, la apertura de una Activity o la participación de algún tipo de elementos de la UI de UI , tales como Dialogs no es posible. Después de algunas investigaciones me di cuenta de que el máximo que puede proporcionar es un mensaje Toast notificando la entrega de registro al servidor. Opcionalmente, se puede usar SharedPreferences para indicar el bloqueo de la aplicación y, en el reinicio de la aplicación, se puede mostrar un Dialog basado en el valor del atributo SharedPreferences y desde allí entregar la excepción previamente detectada (aparentemente, Accra utiliza el mismo enfoque):

 public class FirstAc extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(); sharedPrefValue = pref.getBoolean("DID_APP_CRASH", false); if(sharedPrefValue) dialog.show(); } } 

La excepción se puede guardar como una cadena cuando la aplicación se estrelló con el fragmento de código siguiente:

 StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); exception.printStackTrace(pw); String stStr = sw.toString(); prefEditor.putString("EXCEPTION_CAUGHT", stStr); 

Para resumir, para entregar la excepción no captada a un servidor remoto crear un UncaughtExceptionHandler personalizado y lo más importante mantener una referencia a la UncaughtExceptionHandler predeterminado. En lugar de cerrar de forma abrupta la VM llamando a System.exit() , es más razonable permitir que Android maneje la excepción después de realizar las operaciones personalizadas. Prefiero establecer el controlador de excepciones en el lado de la Application :

 public class CustomApp extends Application { @Override public void onCreate() { super.onCreate(); Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this)); } } 

Dentro de CustomExceptionHandler después de ejecutar el comportamiento personalizado, deje que Android maneje la excepción de la manera predeterminada:

 public class CustomExceptionHandler implements UncaughtExceptionHandler { private CustomApp _app; private UncaughtExceptionHandler _defaultEH; public YolbilExceptionHandler(YolbilApp ac){ _defaultEH = Thread.getDefaultUncaughtExceptionHandler(); _app = ac; } @Override public void uncaughtException(Thread thread, final Throwable ex) { Toast.makeText(_app, "Delivering log...", Toast.LENGTH_LONG).show(); // obtain the Exception info as a String StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); String exStr = sw.toString(); ExceptionServer.getInstance().deliverMessageAsync(exStr, _app); _defaultEH.uncaughtException(thread, ex); } } 

Y aquí está un ejemplo de cómo entregar de forma asíncrona un mensaje al servidor:

 public void deliverMessageAsync(final String msg, final YolbilApp app){ new Thread(new Runnable() { @Override public void run() { HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(SERVER_ADDR); try { Looper.prepare(); Toast.makeText(app, R.string.msg_delivering_log, Toast.LENGTH_LONG).show(); httppost.setHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8"); httpclient.execute(httppost); Toast.makeText(app, "Log delivered ...", Toast.LENGTH_SHORT).show(); Looper.loop(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } 
  • Android - onCreateDialog se bloquea al devolver DatePickerDialog
  • Android - Cambiar el texto del botón positivo del diálogo de EditTextPreference mediante programación
  • ¿Cómo se muestra un diálogo de alerta en Android?
  • NullPointerException al crear un nuevo cuadro de diálogo
  • Diálogo AppCompat con estilo que contiene un fragmento
  • ¿Cómo manejar el cambio de orientación de pantalla cuando el diálogo de progreso y el hilo de fondo están activos?
  • AlertDialog con el selector
  • Android: Margen mínimo de diálogo
  • ¿Cómo mostrar un diálogo para confirmar que el usuario desea salir de una actividad de Android?
  • Cómo crear un diálogo personalizado no deseable en android
  • DialogFragment - Obtener el ancho del diálogo
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.