Показать диалог в `Thread.setDefaultUncaughtExceptionHandler`
Когда мое приложение для Android выдает исключение, я хочу показать пользовательское диалоговое окно, чтобы сообщить пользователю, что что-то случилось неправильно, поэтому я использую Thread.setDefaultUncaughtExceptionHandler
для установки глобального обработчика исключений:
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();
}
});
}
}
Но я обнаружил, что есть любое исключение, AlertDialog
не будет отображать вместо этого блоки приложений и через некоторое время отобразит системный диалог:
X app is not responding. Would you like to close it?
Wait | OK
Что мне теперь делать?
UPDATE
Журнал:
11-16 12:54:16.017: WARN/WindowManager(90): Attempted to add window with non-application token WindowToken{b38bb6a8 token=null}. Aborting.
Кажется, что ошибка исходит от new AlertDialog.Builder(getApplicationContext());
Но это обработчик исключений в подклассе Application
, как я могу установить для него экземпляр действия?
Ответы
Ответ 1
Вы не можете выполнять какую-либо операцию пользовательского интерфейса здесь. Просто запустите другой экран активности/заставки. Передайте намерение дополнительно, чтобы обозначить крушение и показать диалог в этом действии.
/*
* (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
}
Также смотрите:
Android UncaughtExceptionHandler, создающий разрывы AlertDialog
Тост не отображается в UnCaughtExceptionHandler
Как начать работу с UncaughtExceptionHandler, если это основной поток разбился?
Как я это делаю:
У меня есть BaseActivity, который расширяет Activity, а в onCreate активности я устанавливаю UncaughtExceptionHandler. Все мои действия расширяют BaseActivity вместо Activity.
Клавиши
- Вы не можете установить обработчик исключений в
Application.onCreate
, вместо этого вы должны создать BaseActivity
и установить его в методе onCreate
.
- После запуска SplashActivity мы должны позвонить
System.exit(0)
- Мы не можем содержать экземпляр ошибки, чтобы передать его в
SplashActivity
, так как он будет уничтожен, вместо этого мы можем передать некоторое сообщение об ошибке или сохранить его в файле.
Ответ 2
Кажется, что предоставленное решение не работает (по крайней мере, для Android 4.0 и выше). Для всех, кто может быть заинтересован, открытие Activity
или использование каких-либо элементов UI
, таких как Dialogs
, невозможно. После некоторых исследований я понял, что максимум, который вы можете предоставить, - это сообщение Toast
, сообщающее о доставке журнала на сервер. Опционально SharedPreferences
может использоваться для указания сбоя приложения и при перезапуске приложения Dialog
может отображаться на основе значения атрибута SharedPreferences
и оттуда доставлять ранее пойманное исключение (очевидно, Accra использует тот же подход ):
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();
}
}
Исключение можно сохранить как строку, когда приложение разбилось со следующим фрагментом кода:
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
String stStr = sw.toString();
prefEditor.putString("EXCEPTION_CAUGHT", stStr);
Подводя итог, чтобы доставить неперехваченное исключение на удаленный сервер, создайте пользовательский UncaughtExceptionHandler
и, самое главное, сохраните ссылку на значение по умолчанию UncaughtExceptionHandler
. Вместо того, чтобы резко закрыть виртуальную машину, вызвав System.exit()
, более разумно позволить Android обрабатывать исключение после выполнения пользовательских операций. Я предпочитаю установить обработчик исключений со стороны Application
:
public class CustomApp extends Application {
@Override
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this));
}
}
Внутри CustomExceptionHandler
после выполнения пользовательского поведения Android обрабатывает исключение по умолчанию:
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);
}
}
И вот пример, как отправить асинхронно сообщение серверу:
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();
}