【问题标题】:Using Global Exception Handling on android在android上使用全局异常处理
【发布时间】:2011-05-24 13:45:34
【问题描述】:

是否有代码示例,或者关于如何使用Thread.setDefaultUncaughtExceptionHandler 方法的教程?基本上我试图在我的应用程序中显示一个自定义警报对话框,只要抛出异常。是否有可能做到这一点?我知道在屏幕上显示一些东西有点棘手,如果在 UI 线程中抛出异常,但我不知道有什么解决方法。

【问题讨论】:

  • 请问有什么意义吗?标准的 Android 对话框允许用户报告崩溃,这会将堆栈跟踪发送到您的帐户。如果你真的想这样做 - 好吧,只需使用 Theme.Dialog 开始一个新的意图,在附加组件中传递异常信息,然后就可以了。
  • 在我的应用程序中,我使用的是数据库文件。如果应用程序无法连接到数据库,则会抛出异常。我想捕捉这个异常并显示一条自定义消息。这只是一个例子。这样的情况还有很多。因此,我最好在适当的位置捕获所有这些异常。
  • 这听起来不是个好主意。无法连接到数据库是具有特定异常的特定问题。您正在处理任何可能的异常,包括由于代码草率而导致的 NullPointerException。我会强烈建议将 try/catch 块放在捕获特定于数据库的异常的数据库代码周围。
  • 在我的代码中,有很多包含数据库的操作。我试图找到一种方法,而不是每次执行数据库操作时都使用 try catch 块。您是说最好的做法是坚持使用 try catch 块吗?不是一种就地处理这些案件的方法吗?
  • “你是说最好的做法是坚持使用 try catch 块吗?” -- 恕我直言,是的。

标签: android exception-handling


【解决方案1】:

带有解决方案的人的基本示例:)

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

错误处理类:

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

【讨论】:

  • 难道不需要再次抛出异常停止执行,不让线程处于未确定状态吗?
  • 我看不到你的 :D 。在 uncaughtException 中,你可以给出你想要的任何处理方式。
  • @Vaiden:不需要重新抛出异常。操作系统已经终止了线程,因此在您的处理程序完成后不会返回它。
  • 在我这样做之后,我的一些其他活动告诉我Cannot reduce the visibility of the inherited method from BaseActivity。才意识到问题。 BaseActivity 的onCreate 设置为public,但我所有的下降活动onCreate 设置为protected。刚刚也将 BaseActivity 更改为 protected
  • 这只是导致应用程序崩溃时冻结。
【解决方案2】:

这是Mohit Sharmathe answer 的变体,具有以下改进:

  • 错误处理后不会导致应用/服务冻结
  • 让 Android 自行处理正常的错误处理

代码:

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

【讨论】:

  • 对于迟到的问题很抱歉,但您在System.exit() 通话中使用退出代码 2 是否有原因? Android 是否关心退出代码?我似乎找不到这方面的任何信息...
  • @MarkusA.,我不记得我为什么使用那个代码了。也许01 导致应用程序冻结或其他原因。
  • 嗯...我想这可能值得一个单独的问题:stackoverflow.com/questions/30226842/… 让我们看看结果如何...
  • 很好的答案,但我在使用您的答案时遇到了两个问题。首先,如果您让所有活动从 BaseActivity 继承,或者重新实例化从 BaseActivity 继承的活动,则将所有默认处理程序链接在一起:第一个意图:自定义异常处理程序 (cEH) 调用默认异常处理程序 (dEH) 第二个意图:cEH -> 第一个意图的 cEH -> dEH 第三个意图:cEH -> 第二个意图的 cEH -> 第一个意图的 cEH -> dEH ..... 这会导致多次调用您定义的操作并在结束。
  • @Sam 如何向其添加自定义消息。像“应用程序一直忙”而不是默认的“应用程序已停止工作”?
【解决方案3】:

对于那些只想在您的应用在设备上崩溃时(在调试配置中)查看异常详细信息的人。这是应用程序类:

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 线程可能不再工作。

【讨论】:

    【解决方案4】:

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

    不要显示任何内容,实际上 UI 线程可能已经崩溃,请写入日志和/或将详细信息发送到服务器,而不是。您可能想查看this question and its answers

    【讨论】:

      【解决方案5】:

      我只是想指出我到目前为止的经验。我正在使用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);                                                                                                                                                                                                                                                                                                                                                                      
              }                                                                                                                                                                                                                                                                                                                                                                                                                       
       });
      

      handleUncaughtException(thread, e, defaultEH); 写入日志并将调用移交给原始的 UncaughtExceptionHandler。

      所以使用此代码发生的情况如下:

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

      所以这是一个无限增长的链条,导致两个问题:

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

      因此,我又添加了一件事情以最终使这项工作顺利进行:

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

      通过这个解决方案,我们可以确保:

      • 为我们想要的操作添加一个自定义异常处理程序
      • 确保此操作只触发一次
      • 允许垃圾收集器通过调用 finish() 完全处理我们的活动

      【讨论】:

      • 这不是放在Application类里吗?使用您的方法,customExceptionHandlerAttached 将在轮换时重置为 false。
      【解决方案6】:

      如果你想使用这个库

      https://github.com/selimtoksal/Android-Caught-Global-Exception-Library

      在您的活动中创建您的 TransferObject,而不是全部在基本活动中使用

      【讨论】:

        猜你喜欢
        • 2016-11-03
        • 1970-01-01
        • 1970-01-01
        • 2015-12-21
        • 2011-09-29
        • 2011-06-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多