Android?Java?try?catch?失效問題及解決
參考:
- How to catch an Exception from a thread
- Is there a way to make Runnable's run() throw an exception?
- Java捕獲線程異常的幾種方式
如果你在 異常拋出處 的 外層函數(shù) 中添加了 try catch 不生效的話, 就試試下面的辦法吧.
解決辦法
方法一
如果在 異常拋出處 或 外層調(diào)用函數(shù)中 使用了 Runnable run 函數(shù), try catch 需要添在 run 函數(shù)里面, 如下:
new Thread(new Runnable() { @Override public void run() { try { throw new IllegalArgumentException("test exception"); } catch (Exception e) { e.printStackTrace(); } } }).start();
如果使用的是第三方庫, 無法捕獲 Runnable run 函數(shù)中的異常時, 則可在 Runnable 之前添加如下代碼解決(需注意: 此方法在 Android 中子線程可用, 主線程仍會 crash):
// 在調(diào)用第三方庫前先執(zhí)行下面代碼 Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { // 這里就可以捕獲到第三方庫的異常了 } }); // 假如這里是一個第三方庫拋出異常的地方 new Thread(new Runnable() { @Override public void run() { // 子線程 -> 拋出異常 throw Exception("unknown exception"); } }).start();
在 Android 中, 如果無法捕獲 Runnable run 函數(shù)中的異常, 并且是在主線程調(diào)用, 就只能想辦法避免 crash 了.
比如我是在調(diào)用 show 函數(shù)之前有網(wǎng)絡請求, 網(wǎng)絡請求成功后, 此頁面已不在前臺, 才會導致 crash; 可以在網(wǎng)絡請求成功后, 判斷此頁面是否在前臺展示, 再執(zhí)行相關操作.
事情起因
新版上線后, 出現(xiàn)了這個 crash. 經(jīng)排查, 發(fā)現(xiàn) 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(GuideLayout.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)
根據(jù) 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 會調(diào)用到異常拋出的位置 showGuidePage(); ... } }); }
發(fā)現(xiàn)在 show 函數(shù)中, 有關鍵代碼 mParentView.post(runnable), 此時, 異常就是在 run 函數(shù)中調(diào)用的 showGuidePage 中拋出的, 并且這個異常在主線程中, 主線程就會停止掉, 就會 crash!
總結(jié)
起先, 我是自己 throw Exception, 但 try catch 都是正常生效的, 始終無法復現(xiàn)線上的 crash. 后來靈光一閃, 想到在子線程中拋出異常會怎樣呢? 經(jīng)過嘗試, 的確 catch 不到 子線程的 Exception, 具體原因不了解.... 或許我們可以把結(jié)果看成一個定義. 然后就在網(wǎng)上查了相關問題。
結(jié)論如下:
在 Java 中, 線程中的異常是不能拋出到調(diào)用該線程的外部方法中捕獲的.(我覺得這句話可改為, 在 Android 的 Runnable run 函數(shù)中的異常是不能拋出到外部方法中捕獲的. 因為在 Android 的主線程中使用 Runnable run 函數(shù), 不在 run 函數(shù)中 try catch 的話, 仍會 crash!)
因為線程是獨立執(zhí)行的代碼片斷, 線程的問題應該由線程自己來解決, 而不要委托到外部. 基于這樣的設計理念, 在 Java 中, 線程方法的異常都應該在線程代碼邊界之內(nèi)(run 方法內(nèi))進行 try catch 并處理掉. 換句話說, 我們不能捕獲從線程中逃逸的異常.
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Ubuntu中為Android增加硬件抽象層(HAL)模塊訪問Linux內(nèi)核驅(qū)動程序
本文主要介紹在Ubuntu上為Android HAL模塊訪問Linux內(nèi)核驅(qū)動程序,這里給大家提供方法和一個小的測試程序代碼,以及常遇到的問題和解決方法,有需要的小伙伴可以參考下2016-08-08TextView顯示文本控件兩種方法 TextView顯示link的方法
這篇文章主要為大家詳細介紹了TextView顯示文本控件兩種方法,TextView顯示link的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08