开发者

Android Java try catch 失效问题及解决

开发者 https://www.devze.com 2022-11-29 12:46 出处:网络 作者: 吃饱很舒服
目录解决办法方法一事情起因总结参考:HowtocatchanExceptionfromathreadIsthereawaytomakeRunnablesrun()thro...
目录
  • 解决办法
    • 方法一
  • 事情起因
    • 总结

      参考:

      • How to catch an Exception from a thread
      • Is there a way to make Runnable's run() throw an exception?
      • Java捕获线程异常的几种方式

      如果你在 异常抛出处 的 外层函数 中添加了 try catch 不生效的话, 就试试下面的办法吧.

      解决办法

      方法一

      如果在 异常抛出处 或 外层调用函数中 使用了 Runnable run 函数, try catch 需要添在 run 函数里面, 如下:

      new Thread(new Runnable() {
                      @Override
             http://www.devze.com         p编程客栈ublic void run() {
                          try {
                              throw new IllegalArgumentException("test exception");
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                      }
                  }).start();

      如果使用的是第三方库, 无法捕获 Runnable run 函数中的异常时, 则可在 Runnable 之前添加如下代码解决(需注意: 此方法在 android 中子线程可用, 主线程仍会 crash):

      // 在调用第三方库前先执行下面代码
      Thread.setDefaultUncaughtExceptionHandler(new Thrpythonead.UncaughtExceptionHandler() {
                      @Overphpride
                      public void uncaughtException(Thread t, Throwable e) {
                           // 这里就可以捕获到第三方库的异常了   
                      }
                  });
       
       
      // 假如这里是一个第三方库抛出异常的地方
      new Thread(new Runnable() {
                      @Override
                      public void run() {
                          // 子线程 -> 抛出异常
                  开发者_JS教程        throw Exception("unknown exception");
                      }
                  }).start();

      在 Android 中, 如果无法捕获 Runnable run 函数中的异常, 并且是在主线程调用, 就只能想办法避免 crash 了. 

      比如我是在调用 show 函数之前有网络请求, 网络请求成功后, 此页面已不在前台, 才会导致 crash; 可以在网络请求成功后, 判断此页面是否在前台展示, 再执行相关操作.

      事情起因

      新版上线后, 出现了这个 crash. 经排查, 发现 crash 是从第三方库中抛出的, 位置如下: 

      2021-12-23 17:39:57.408 3535-3535/com.podbean.app.podcast E/AndroidRuntime: FATAL EXCEPTION: main

          Process: com.podbean.app.podcast, PID: 3535

          java.lang.IllegalArgumentException: the view is not showing in the window!

              at com.app.hubert.guide.util.ViewUtils.getLocationInView(ViewUtils.java:47)

              at com.app.hubert.guide.model.HighlightView.fetchLocation(HighlightView.java:77)

              at com.app.hubert.guide.model.HighlightView.getRectF(HighlightView.java:67)

              at com.app.hubert.guide.model.RelativeGuide.getMarginInfo(RelativeGuide.java:90)

              at com.app.hubert.guide.model.RelativeGuide.getGuideLayout(RelativeGuide.java:76)

              at com.app.hubert.guide.core.GuideLayout.addCustomToLayout(Gu编程客栈ideLayout.java:227)

              at com.app.hubert.guide.core.GuideLayout.onAttachedToWindow(GuideLayout.java:185)

              at android.view.View.dispatchAttachedToWindow(View.java:20479)

              at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3489)

              at android.view.ViewGroup.addViewInner(ViewGroup.java:5278)

              at android.view.ViewGroup.addView(ViewGroup.java:5064)

              at android.view.ViewGroup.addView(ViewGroup.java:5036)

              at com.app.hubert.guide.core.Controller.showGuidePage(Controller.java:175)

              at com.app.hubert.guide.core.Controller.Access$200(Controller.java:39)

              at com.app.hubert.guide.core.Controller$1.run(Controller.java:118)

              at android.os.Handler.handleCallback(Handler.java:938)

              at android.os.Handler.dispatchMessage(Handler.java:99)

              at android.os.Looper.loop(Looper.java:223)

              at android.app.ActivityThread.main(ActivityThread.java:7664)

              at java.lang.reflect.Method.invoke(Native Method)

              at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)

              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

      根据 log 信息, 最终我找到了这里

      // ViewUitls.java
      public static Rect getLocationInView(View parent, View child) {
          ...
          if (tmp == null) {
              // 异常抛出位置
              throw new IllegalArgumentException("the view is not showing in the window!");
                  }
          ...
      }
       
       
      // Controller.java
      public void show() {
              ...
              // 使用 Runnable run 位置
              mParentView.post(new Runnable() {
                  @Override
                  public void run() {
                      ...
                      // showGuidePage 会调用到异常抛出的位置
                      showGuidePage();
                      ...
                  }
              });
          }

      发现在 show 函数中, 有关键代码 mParentView.post(runnable), 此时, 异常就是在 run 函数中调用的 showGuidePage 中抛出的, 并且这个异常在主线程中, 主线程就会停止掉, 就会 crash!

      总结

      起先, 我是自己 throw Exception, 但 try catch 都是正常生效的, 始终无法复现线上的 crash. 后来灵光一闪, 想到在子线程中抛出异常会怎样呢? 经过尝试, 的确 catch 不到 子线程的 Exception, 具体原因不了解.... 或许我们可以把结果看成一个定义. 然后就在网上查了相关问题。

      结论如下:

      在 Java 中, 线程中的异常是不能抛出到调用该线程的外部方法中捕获的.(我觉得这句话可改为, 在 Android 的 Runnable run 函数中的异常是不能抛出到外部方法中捕获的. 因为在 Android 的主线程中使用 Runnable run 函数, 不在 run 函数中 try catch 的话, 仍会 crash!)

      因为线程是独立执行的代码片断, 线程的问题应该由线程自己来解决, 而不要委托到外部. 基于这样的设计理念, 在 Java 中, 线程方法的异常都应该在线程代码边界之内(run 方法内)进行 try catch 并处理掉. 换句话说, 我们不能捕获从线程中逃逸的异常.

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号