在 android 上使用全局异常处理

是否有关于如何使用 Thread.setDefaultUncaughtExceptionHandler方法的代码示例或教程?基本上,我试图在我的应用程序中显示一个自定义的警报对话框,无论何时抛出异常。有可能做到吗?我知道在界面线程中抛出异常时,在屏幕上显示一些东西是有点麻烦的,但我不知道有什么办法可以解决这个问题。

69502 次浏览

请记住,在设置处理程序之前检查了 The RuntimePermission("setDefaultUncaughtExceptionHandler"),并确保随后通过抛出未捕获的异常导致进程停止,因为事情可能处于不确定状态。

不要显示任何东西,事实上 UI 线程可能已经崩溃了,请写一个日志并/或将详细信息发送到服务器。你可能想看看 这个问题及其答案

有人带着解决方案来到这个页面的基本示例:)

public class ChildActivity extends BaseActivity {
@SuppressWarnings("unused")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int a=1/0;
}
}

Class for handling error:

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 !!!");
}
});
}
}

Here's a variant of 答案 by Mohit Sharma with the following improvements:

  • 不会导致应用程序/服务在错误处理后冻结
  • Lets Android do its normal error handling after your own

密码:

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
}
});
}
}

如果你想使用这个图书馆

Https://github.com/selimtoksal/android-caught-global-exception-library

create your TransferObject not all in your activities just use in Base activity

我只是想指出我目前的经历。我正在使用 https://stackoverflow.com/a/26560727/2737240建议的解决方案将异常刷新到我的日志文件中,然后将控制权交给默认的异常处理程序。

然而,我的结构是这样的:

          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);
}
});

where handleUncaughtException(thread, e, defaultEH); writes to the log and hands the call over to the original UncaughtExceptionHandler.

使用这个代码所发生的事情如下:

  • 活动 A 被实例化
  • 新的默认异常处理程序(DEH)现在是我的日志处理程序 + 旧的 DEH
  • Activity B is instantiated
  • 新的 DEH 现在是我的日志处理程序 + 老的 DEH (日志处理程序 + 原始 DEH)
  • 活动 C 已实例化
  • 新的 DEH 现在是我的日志处理程序 + 老的 DEH (日志处理程序 + 日志处理程序 + 原始 DEH)

所以这是一个无限增长的链条,引起了两个问题:

  1. 指定的自定义代码(在我的情况下写入日志文件)将被多次调用,这不是所需的操作。
  2. 即使在活动完成之后,defaultAi 的引用仍然保留在堆中,因为它被引用链使用,所以最糟糕的情况就是内存不足异常。

因此,我增加了一件事情,最终使这个工作没有问题:

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;
}
}

有了这个解决方案,我们可以确保:

  • 为我们所需的操作添加一个自定义异常处理程序
  • ensure that this action is only triggered once
  • 允许垃圾收集器通过调用 Finish ()来完全处理我们的活动

For those who just want to see exception details when your app crashes on device (in debug config). This is application class:

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);
}
});
}

它使用外部应用程序作为你的 UI 线程可能不再工作。