目錄總結(jié)
- 00.異常處理幾個(gè)常用api
- 01.UncaughtExceptionHandler
- 02.Java線程處理異常分析
- 03.Android中線程處理異常分析
- 04.為何使用setDefaultUncaughtExceptionHandler
前沿
- 上一篇整體介紹了crash崩潰庫(kù)崩潰重啟,崩潰記錄記錄吊说,查看以及分享日志等功能。
- 項(xiàng)目地址:https://github.com/yangchong211/YCAndroidTool
- 歡迎star
00.異常處理幾個(gè)常用api
- setUncaughtExceptionHandler
- public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
- 設(shè)置該線程由于未捕獲到異常而突然終止時(shí)調(diào)用的處理程序。
- 通過(guò)明確設(shè)置未捕獲到的異常處理程序,線程可以完全控制它對(duì)未捕獲到的異常作出響應(yīng)的方式玻蝌。
- 如果沒有設(shè)置這樣的處理程序灌危,則該線程的 ThreadGroup 對(duì)象將充當(dāng)其處理程序。
- public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
- 返回該線程由于未捕獲到異常而突然終止時(shí)調(diào)用的處理程序抽碌。
- 如果該線程尚未明確設(shè)置未捕獲到的異常處理程序,則返回該線程的 ThreadGroup 對(duì)象决瞳,除非該線程已經(jīng)終止货徙,在這種情況下,將返回 null皮胡。
- public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
- setDefaultUncaughtExceptionHandler
- public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
- 設(shè)置當(dāng)線程由于未捕獲到異常而突然終止痴颊,并且沒有為該線程定義其他處理程序時(shí)所調(diào)用的默認(rèn)處理程序。
- 未捕獲到的異常處理首先由線程控制屡贺,然后由線程的 ThreadGroup 對(duì)象控制蠢棱,最后由未捕獲到的默認(rèn)異常處理程序控制锌杀。
- 如果線程不設(shè)置明確的未捕獲到的異常處理程序,并且該線程的線程組(包括父線程組)未特別指定其 uncaughtException 方法泻仙,則將調(diào)用默認(rèn)處理程序的 uncaughtException 方法糕再。
-- 通過(guò)設(shè)置未捕獲到的默認(rèn)異常處理程序,應(yīng)用程序可以為那些已經(jīng)接受系統(tǒng)提供的任何“默認(rèn)”行為的線程改變未捕獲到的異常處理方式(如記錄到某一特定設(shè)備或文件)玉转。
- public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
- 返回線程由于未捕獲到異常而突然終止時(shí)調(diào)用的默認(rèn)處理程序突想。
- 如果返回值為 null,則沒有默認(rèn)處理程序究抓。
- public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
- Thread.UncaughtExceptionHandler
- public static interface Thread.UncaughtExceptionHandler
- 所有已知實(shí)現(xiàn)類:ThreadGroup
- 當(dāng) Thread 因未捕獲的異常而突然終止時(shí)猾担,調(diào)用處理程序的接口。
- 當(dāng)某一線程因未捕獲的異常而即將終止時(shí)刺下,Java 虛擬機(jī)將使用 Thread.getUncaughtExceptionHandler() 查詢?cè)摼€程以獲得其 UncaughtExceptionHandler 的線程绑嘹,并調(diào)用處理程序的 uncaughtException 方法,將線程和異常作為參數(shù)傳遞橘茉。
- 如果某一線程沒有明確設(shè)置其 UncaughtExceptionHandler工腋,則將它的 ThreadGroup 對(duì)象作為其 UncaughtExceptionHandler。
- 如果 ThreadGroup 對(duì)象對(duì)處理異常沒有什么特殊要求畅卓,那么它可以將調(diào)用轉(zhuǎn)發(fā)給默認(rèn)的未捕獲異常處理程序夷蚊。
- public static interface Thread.UncaughtExceptionHandler
01.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.
- 翻譯后大概的意思是
- UncaughtExceptionHandler接口用于處理因?yàn)橐粋€(gè)未捕獲的異常而導(dǎo)致一個(gè)線程突然終止問(wèn)題。
- 當(dāng)一個(gè)線程因?yàn)橐粋€(gè)未捕獲的異常即將終止時(shí)髓介,Java虛擬機(jī)將通過(guò)調(diào)用getUncaughtExceptionHandler() 函數(shù)去查詢?cè)摼€程的UncaughtExceptionHandler并調(diào)用處理器的 uncaughtException方法將線程及異常信息通過(guò)參數(shù)的形式傳遞進(jìn)去惕鼓。如果一個(gè)線程沒有明確設(shè)置一個(gè)UncaughtExceptionHandler,那么ThreadGroup對(duì)象將會(huì)代替UncaughtExceptionHandler完成該行為唐础。如果ThreadGroup沒有明確指定處理該異常箱歧,ThreadGroup將轉(zhuǎn)發(fā)給默認(rèn)的處理未捕獲的異常的處理器。
- 異骋慌颍回調(diào):uncaughtException
- uncaughtException (Thread t, Throwable e) 是一個(gè)抽象方法呀邢,當(dāng)給定的線程因?yàn)榘l(fā)生了未捕獲的異常而導(dǎo)致終止時(shí)將通過(guò)該方法將線程對(duì)象和異常對(duì)象傳遞進(jìn)來(lái)。
- 設(shè)置默認(rèn)未捕獲異常處理器:setDefaultUncaughtExceptionHandler
- void setDefaultUncaughtExceptionHandler (Thread.UncaughtExceptionHandler eh)
- 設(shè)置一個(gè)處理者當(dāng)一個(gè)線程突然因?yàn)橐粋€(gè)未捕獲的異常而終止時(shí)將自動(dòng)被調(diào)用豹绪。
- 未捕獲的異常處理的控制第一個(gè)被當(dāng)前線程處理价淌,如果該線程沒有捕獲并處理該異常,其將被線程的ThreadGroup對(duì)象處理瞒津,最后被默認(rèn)的未捕獲異常處理器處理蝉衣。
- 通過(guò)設(shè)置默認(rèn)的未捕獲異常的處理器,對(duì)于那些早已被系統(tǒng)提供了默認(rèn)的未捕獲異常處理器的線程巷蚪,一個(gè)應(yīng)用可以改變處理未捕獲的異常的方式病毡,例如記錄到指定的設(shè)備或者文件。
- handler將會(huì)報(bào)告線程終止和不明原因異常這個(gè)情況屁柏,如果沒有自定義handler啦膜, 線程管理組就被默認(rèn)為報(bào)告異常的handler有送。
- ThreadHandler 這個(gè)類就是實(shí)現(xiàn)了UncaughtExceptionHandler這個(gè)接口,偽代碼代碼如下所示
public class ThreadHandler implements Thread.UncaughtExceptionHandler { private Thread.UncaughtExceptionHandler mDefaultHandler; private boolean isInit = false; /** * CrashHandler實(shí)例 */ private static ThreadHandler INSTANCE; /** * 獲取CrashHandler實(shí)例 ,單例模式 */ public static ThreadHandler getInstance() { if (INSTANCE == null) { synchronized (CrashHandler.class) { if (INSTANCE == null) { INSTANCE = new ThreadHandler(); } } } return INSTANCE; } /** * 當(dāng)UncaughtException發(fā)生時(shí)會(huì)轉(zhuǎn)入該函數(shù)來(lái)處理 * 該方法來(lái)實(shí)現(xiàn)對(duì)運(yùn)行時(shí)線程進(jìn)行異常處理 */ @Override public void uncaughtException(Thread t, Throwable e) { if (mDefaultHandler != null) { //收集完信息后僧家,交給系統(tǒng)自己處理崩潰 //uncaughtException (Thread t, Throwable e) 是一個(gè)抽象方法 //當(dāng)給定的線程因?yàn)榘l(fā)生了未捕獲的異常而導(dǎo)致終止時(shí)將通過(guò)該方法將線程對(duì)象和異常對(duì)象傳遞進(jìn)來(lái)雀摘。 mDefaultHandler.uncaughtException(t, e); } else { //否則自己處理 } } /** * 初始化,注冊(cè)Context對(duì)象, * 獲取系統(tǒng)默認(rèn)的UncaughtException處理器, * 設(shè)置該CrashHandler為程序的默認(rèn)處理器 * @param ctx */ public void init(Application ctx) { if (isInit){ return; } //獲取系統(tǒng)默認(rèn)的UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //將當(dāng)前實(shí)例設(shè)為系統(tǒng)默認(rèn)的異常處理器 //設(shè)置一個(gè)處理者當(dāng)一個(gè)線程突然因?yàn)橐粋€(gè)未捕獲的異常而終止時(shí)將自動(dòng)被調(diào)用。 //未捕獲的異常處理的控制第一個(gè)被當(dāng)前線程處理八拱,如果該線程沒有捕獲并處理該異常届宠,其將被線程的ThreadGroup對(duì)象處理,最后被默認(rèn)的未捕獲異常處理器處理乘粒。 Thread.setDefaultUncaughtExceptionHandler(this); isInit = true; } }
02.Java線程處理異常分析
- 線程出現(xiàn)未捕獲異常后,JVM將調(diào)用Thread中的dispatchUncaughtException方法把異常傳遞給線程的未捕獲異常處理器伤塌。
public final void dispatchUncaughtException(Throwable e) { Thread.UncaughtExceptionHandler initialUeh = Thread.getUncaughtExceptionPreHandler(); if (initialUeh != null) { try { initialUeh.uncaughtException(this, e); } catch (RuntimeException | Error ignored) { // Throwables thrown by the initial handler are ignored } } getUncaughtExceptionHandler().uncaughtException(this, e); } public static UncaughtExceptionHandler getUncaughtExceptionPreHandler() { return uncaughtExceptionPreHandler; }
- Thread中存在兩個(gè)UncaughtExceptionHandler灯萍。一個(gè)是靜態(tài)的defaultUncaughtExceptionHandler,另一個(gè)是非靜態(tài)uncaughtExceptionHandler每聪。
// null unless explicitly set private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
- defaultUncaughtExceptionHandler:設(shè)置一個(gè)靜態(tài)的默認(rèn)的UncaughtExceptionHandler旦棉。來(lái)自所有線程中的Exception在拋出并且未捕獲的情況下,都會(huì)從此路過(guò)药薯。進(jìn)程fork的時(shí)候設(shè)置的就是這個(gè)靜態(tài)的defaultUncaughtExceptionHandler绑洛,管轄范圍為整個(gè)進(jìn)程。
- uncaughtExceptionHandler:為單個(gè)線程設(shè)置一個(gè)屬于線程自己的uncaughtExceptionHandler童本,轄范圍比較小真屯。
- 沒有設(shè)置uncaughtExceptionHandler怎么辦?
- 如果沒有設(shè)置uncaughtExceptionHandler穷娱,將使用線程所在的線程組來(lái)處理這個(gè)未捕獲異常绑蔫。
- 線程組ThreadGroup實(shí)現(xiàn)了UncaughtExceptionHandler,所以可以用來(lái)處理未捕獲異常泵额。ThreadGroup類定義:
private ThreadGroup group; //可以發(fā)現(xiàn)ThreadGroup類是集成Thread.UncaughtExceptionHandler接口的 class ThreadGroup implements Thread.UncaughtExceptionHandler{}
- 然后看一下ThreadGroup中實(shí)現(xiàn)uncaughtException(Thread t, Throwable e)方法配深,代碼如下
- 默認(rèn)情況下,線程組處理未捕獲異常的邏輯是嫁盲,首先將異常消息通知給父線程組篓叶,
- 然后嘗試?yán)靡粋€(gè)默認(rèn)的defaultUncaughtExceptionHandler來(lái)處理異常,
- 如果沒有默認(rèn)的異常處理器則將錯(cuò)誤信息輸出到System.err羞秤。
- 也就是JVM提供給我們?cè)O(shè)置每個(gè)線程的具體的未捕獲異常處理器缸托,也提供了設(shè)置默認(rèn)異常處理器的方法。
public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } }
03.Android中線程處理異常分析
- 在Android平臺(tái)中瘾蛋,應(yīng)用進(jìn)程fork出來(lái)后會(huì)為虛擬機(jī)設(shè)置一個(gè)未截獲異常處理器嗦董, 即在程序運(yùn)行時(shí),如果有任何一個(gè)線程拋出了未被截獲的異常瘦黑, 那么該異常最終會(huì)拋給未截獲異常處理器處理京革。
- 具體可以找到RuntimeInit類奇唤,然后在找到KillApplicationHandler類。首先看該類的入口main方法--->commonInit()--->匹摇,然后接著往下走咬扇,找到setDefaultUncaughtExceptionHandler代碼如下所示
- 如果報(bào)告崩潰,不要再次進(jìn)入——避免無(wú)限循環(huán)廊勃。如果ActivityThread分析器在此時(shí)運(yùn)行懈贺,我們殺死進(jìn)程,內(nèi)存中的緩沖區(qū)將丟失坡垫。并且打開崩潰對(duì)話框
- 最后會(huì)執(zhí)行finally中殺死進(jìn)程的方法干掉app
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler)); private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler { private final LoggingHandler mLoggingHandler; @Override public void uncaughtException(Thread t, Throwable e) { try { if (mCrashing) return; mCrashing = true; if (ActivityThread.currentActivityThread() != null) { ActivityThread.currentActivityThread().stopProfiling(); } // Bring up crash dialog, wait for it to be dismissed ActivityManager.getService().handleApplicationCrash( mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e)); } catch (Throwable t2) { if (t2 instanceof DeadObjectException) { // System process is dead; ignore } else { try { Clog_e(TAG, "Error reporting crash", t2); } catch (Throwable t3) { // Even Clog_e() fails! Oh well. } } } finally { // Try everything to make sure this process goes away. Process.killProcess(Process.myPid()); System.exit(10); } } }
- UncaughtExceptionHandler存在于Thread中.當(dāng)異常發(fā)生且未捕獲時(shí)梭灿。異常會(huì)透過(guò)UncaughtExceptionHandler拋出。并且該線程會(huì)消亡冰悠。所以在Android中子線程死亡是允許的堡妒。主線程死亡就會(huì)導(dǎo)致ANR。
- 所以其實(shí)在fork出app進(jìn)程的時(shí)候溉卓,系統(tǒng)已經(jīng)為app設(shè)置了一個(gè)異常處理皮迟,并且最終崩潰后會(huì)直接導(dǎo)致執(zhí)行該handler的finallly方法最后殺死app直接退出app。如果你要自己處理桑寨,你可以自己實(shí)現(xiàn)Thread.UncaughtExceptionHandler伏尼。
04.為何使用setDefaultUncaughtExceptionHandler
- Thread.UncaughtExceptionHandler 接口代碼如下所示
@FunctionalInterface public interface UncaughtExceptionHandler { void uncaughtException(Thread t, Throwable e); }
- UncaughtExceptionHandler 未捕獲異常處理接口,當(dāng)一個(gè)線程由于一個(gè)未捕獲異常即將崩潰時(shí)尉尾,JVM 將會(huì)通過(guò) getUncaughtExceptionHandler() 方法獲取該線程的 UncaughtExceptionHandler爆阶,并將該線程和異常作為參數(shù)傳給 uncaughtException()方法。
- 如果沒有顯式設(shè)置線程的 UncaughtExceptionHandler沙咏,那么會(huì)將其 ThreadGroup 對(duì)象會(huì)作為 UncaughtExceptionHandler扰她。
- 如果其 ThreadGroup 對(duì)象沒有特殊的處理異常的需求,那么就會(huì)調(diào) getDefaultUncaughtExceptionHandler() 方法獲取默認(rèn)的 UncaughtExceptionHandler 來(lái)處理異常芭碍。
- 難道要為每一個(gè)線程創(chuàng)建UncaughtExceptionHandler嗎徒役?
- 應(yīng)用程序通常都會(huì)創(chuàng)建很多線程,如果為每一個(gè)線程都設(shè)置一次 UncaughtExceptionHandler 未免太過(guò)麻煩窖壕。
- 既然出現(xiàn)未處理異常后 JVM 最終都會(huì)調(diào) getDefaultUncaughtExceptionHandler()忧勿,那么我們可以在應(yīng)用啟動(dòng)時(shí)設(shè)置一個(gè)默認(rèn)的未捕獲異常處理器。即調(diào)用Thread.setDefaultUncaughtExceptionHandler(handler)
- setDefaultUncaughtExceptionHandler被調(diào)用多次如何理解瞻讽?
- Thread.setDefaultUncaughtExceptionHandler(handler) 方法如果被多次調(diào)用的話鸳吸,會(huì)以最后一次傳遞的 handler 為準(zhǔn),所以如果用了第三方的統(tǒng)計(jì)模塊速勇,可能會(huì)出現(xiàn)失靈的情況晌砾。對(duì)于這種情況,在設(shè)置默認(rèn) hander 之前烦磁,可以先通過(guò) getDefaultUncaughtExceptionHandler() 方法獲取并保留舊的 hander养匈,然后在默認(rèn) handler 的uncaughtException 方法中調(diào)用其他 handler 的 uncaughtException 方法哼勇,保證都會(huì)收到異常信息。