【Android】为啥子线程抛出异常主线程会崩溃?UncaughtExceptionHandler
學而不思則罔,思而不學則殆
【Android】為啥子線程拋出異常主線程會崩潰?UncaughtExceptionHandler
- 引言
- 官方解釋
- 測試
- 測試uncaughtException的調用鏈
- 獲取默認UncaughtExceptionHandler
- 測試Java
- 測試Android
- 源碼分析+找源碼
- 疑問三連
- Demo
- 總結
引言
UncaughtExceptionHandler是Thread內部的一個接口:
public interface UncaughtExceptionHandler {/*** Method invoked when the given thread terminates due to the given uncaught exception.* 方法在給定線程由于給定未捕獲異常而終止時調用。* Any exception thrown by this method will be ignored by the Java Virtual Machine.* 此方法引發的任何異常都將被Java虛擬機忽略。 */void uncaughtException(Thread t, Throwable e);}UncaughtExceptionHandler
Caught - 捕獲 Uncaught - 未捕獲
Exception - 異常
Handler - 處理者
UncaughtExceptionHandler - 未捕獲異常處理者,所以望文生義,使用來處理未被捕獲的異常
官方解釋
Interface for handlers invoked when a Thread abruptly terminates due to an uncaught exception.When a thread is about to terminate due to an uncaught exception the Java Virtual Machine will query the thread for its UncaughtExceptionHandler using getUncaughtExceptionHandler and will invoke the handler's uncaughtException method, passing the thread and the exception as arguments. If a thread has not had its UncaughtExceptionHandler explicitly set, then its ThreadGroup object acts as its UncaughtExceptionHandler. If the ThreadGroup object has no special requirements for dealing with the exception, it can forward the invocation to the default uncaught exception handler. 在線程由于未捕獲異常而突然終止時調用的處理程序的接口。 調用時通過查詢getUncaughtExceptionHandler 如果一個線程還沒有顯式設置它的UncaughtExceptionHandler,那么它的ThreadGroup對象就充當它的UncaughtExceptionHandler。 如果ThreadGroup對象沒有處理異常的特殊要求,它可以將調用轉發給默認的未捕獲異常處理程序。后面兩句主要是描述了使用UncaughtExceptionHandler的順序邏輯。
一般有三種UncaughtExceptionHandler:
| 線程私有的UncaughtExceptionHandler | 最高 |
| ThreadGroup的UncaughtExceptionHandler | 其次 |
| 靜態默認的UncaughtExceptionHandler | 最后 |
當然如果最后都沒有UncaughtExceptionHandler處理這種未捕獲的異常,就默認處理,打印錯誤堆棧異常。
測試
測試uncaughtException的調用鏈
設置當前線程的未捕獲異常處理器,通過setUncaughtExceptionHandler。然后再線程中拋出異常,測試結果.
private static void testSetUncaughtExceptionHandler() {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {int y = new Random().nextInt(2); //這里有可能 y = 0System.out.println(y);int x = 10 / y; //拋出異常System.out.println("testChild end");}});thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("uncaughtException:" + Thread.currentThread() + " " + t + " " + e);}});thread.start();try {Thread.sleep(2 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Main end " + Thread.currentThread());}以下代碼是我的測試結果:
可以看到,當捕獲到未被處理的異常,系統把線程和異常作為參數回調了uncaughtException方法。根據斷點的路勁我們知道該回調的調用起點是dispatchUncaughtException:
/*** Dispatch an uncaught exception to the handler. This method is* intended to be called only by the JVM.*/private void dispatchUncaughtException(Throwable e) {getUncaughtExceptionHandler().uncaughtException(this, e);}官方解釋:
向處理程序發送未捕獲的異常,該方法只被JVM調用
當我們不設置UncaughtExceptionHandler的時候,結果如下:
Java getDefaultUncaughtExceptionHandler:null 0 Exception in thread "Thread-0" java.lang.ArithmeticException: / by zeroat com.thread.UncaughtExceptionHandlerTest$1.run(UncaughtExceptionHandlerTest.java:22)at java.lang.Thread.run(Thread.java:748) Main end Thread[main,5,main]結論結果我們知道兩點結論:
那么這里就到了本篇文章需要討論的點,為啥Android是子線程崩潰后,整個進程都跟著崩潰了呢?
接下來進入分析的重點,Android和Java的默認UncaughtExceptionHandler
獲取默認UncaughtExceptionHandler
測試Java
public static void main(String[] args) {Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();System.out.println("Java getDefaultUncaughtExceptionHandler:" + uncaughtExceptionHandler);}以上代碼測試結果:
Java getDefaultUncaughtExceptionHandler:null測試Android
public void getDefaultUncaughtExceptionHandler(View view) {Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();Log.d("zhangyu20201220", "Android getDefaultUncaughtExceptionHandler:" + uncaughtExceptionHandler);}以上代碼測試結果:
Android getDefaultUncaughtExceptionHandler:com.android.internal.os.RuntimeInit$KillApplicationHandler@45c7407通過上面的測試我們得出結論:
Java沒有默認UncaughtExceptionHandler ,Android有默認的UncaughtExceptionHandler (RuntimeInit$KillApplicationHandler)
接下來就要分析Andorid的默認UncaughtExceptionHandler 。
源碼分析+找源碼
我們先貼出源碼:
public class RuntimeInit {.../*** Logs a message when a thread encounters an uncaught exception. By* default, {@link KillApplicationHandler} will terminate this process later,* but apps can override that behavior.*/private static class LoggingHandler implements Thread.UncaughtExceptionHandler {public volatile boolean mTriggered = false;@Overridepublic void uncaughtException(Thread t, Throwable e) {//...打印log信息...}}}/*** Handle application death from an uncaught exception. The framework* catches these for the main threads, so this should only matter for* threads created by applications. Before this method runs, the given* instance of {@link LoggingHandler} should already have logged details* (and if not it is run first).*/private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {private final LoggingHandler mLoggingHandler;/*** Create a new KillApplicationHandler that follows the given LoggingHandler.* If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called* on the created instance without {@code loggingHandler} having been triggered,* {@link LoggingHandler#uncaughtException(Thread, Throwable)* loggingHandler.uncaughtException} will be called first.** @param loggingHandler the {@link LoggingHandler} expected to have run before* this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}* is being called.*/public KillApplicationHandler(LoggingHandler loggingHandler) {this.mLoggingHandler = Objects.requireNonNull(loggingHandler);}@Overridepublic void uncaughtException(Thread t, Throwable e) {try {ensureLogging(t, e); //打印log...} catch (Throwable t2) {...} finally {// Try everything to make sure this process goes away.Process.killProcess(Process.myPid()); // 殺死當前進程System.exit(10);}}......} }刪除中間的一些干擾代碼,只保留本篇文章關注的重點,進程。我們可以看到在KillApplicationHandler 是的接口中,uncaughtException方法在finally的地方,Kill掉了整個進程。這也就是為啥子線程崩潰整個進程也崩潰的原因。
他們的類圖如下:
疑問三連
那么Android為什么要這個做呢?
思考不是很全,我的理解Android考慮到安全性,所以這么設計的。
或者那位讀者大佬有其他的理解,歡迎評論指出,大家一起學習。
那么可以做到Android是子線程崩潰后,整個進程不崩潰嗎?
可以的,線程可以設置的私有UncaughtExceptionHandler,也可以設置ThreadGroup ,也可以設置DefaultUncaughtExceptionHandler來實現自己的邏輯
該機制的應用有哪些?
目前我知道的有,項目中通過這個方法獲取異常,收起異常崩潰信息,進行上傳和分析。比如常用的CrashHandler,Bugly等崩潰日志記錄框架
Demo
https://github.com/aJanefish/ViewTest
總結
總結一下整篇文章的知識點:
總結
以上是生活随笔為你收集整理的【Android】为啥子线程抛出异常主线程会崩溃?UncaughtExceptionHandler的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 鸿蒙 手游sdk 开发教程
- 下一篇: android sina oauth2.