日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android Crash战斗日记(一、原理篇)

發布時間:2025/6/15 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Crash战斗日记(一、原理篇) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

Crash估計是所有Android開發者的一塊心病,無論是新手小白還是高手大牛,都無法避免遇到Crash。但是Crash是怎么產生的呢,這篇將深入的講解Crash。

一、異常

說到Crash,得先從異常講起,NullPointerException是大家最熟悉的異常之一,下面這個圖片就Bugly上面的一個NullPointerException:


先看看類結構:


發現其實內部什么都沒有,只是繼承了RuntimeException,看起來好像完全沒有意義,這種形式的設計更多的是將類本身當作一個類型作為判斷。下圖是java標注庫中的類繼承圖。


  • Throwable 類是 Java 語言中所有錯誤或異常的超類。只有當對象是此類(或其子類之一)的實例時,才能通過 Java 虛擬機或者 Java throw 語句拋出。類似地,只有此類或其子類之一才可以是 catch 子句中的參數類型。
  • Error 是 Throwable 的子類,用于指示合理的應用程序不應該試圖捕獲的嚴重問題。大多數這樣的錯誤都是異常條件。雖然 ThreadDeath 錯誤是一個“正規”的條件,但它也是 Error 的子類,因為大多數應用程序都不應該試圖捕獲它。
  • Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件。
  • RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。可能在執行方法期間拋出但未被捕獲的 RuntimeException 的任何子類都無需在 throws 子句中進行聲明。
  • RuntimeException是一種Unchecked Exception,即表示編譯器不會檢查程序是否對RuntimeException作了處理,在程序中不必捕獲RuntimException類型的異常,也不必在方法體聲明拋出RuntimeException類。一般來說,RuntimeException發生的時候,表示程序中出現了編程錯誤,所以應該找出錯誤修改程序,而不是去捕獲RuntimeException。常見的RuntimeException有NullPointException、ClassCastException、IllegalArgumentException、IndexOutOfBoundException等。
  • CheckedException是相對于Unchecked Exception而言的,Java中并沒有一個名為Checked Exception的類。它是在編程中使用最多的Exception,所有繼承自Exception并且不是RuntimeException的異常都是Checked Exception。JAVA 語言規定必須對checked Exception作處理,編譯器會對此作檢查,要么在方法體中聲明拋出checked Exception,要么使用catch語句捕獲checked Exception進行處理,不然不能通過編譯。常用的Checked Exception有IOException、ClassNotFoundException等。
  • 二、異常產生過程

    異常產生的過程需要從虛擬機講起,虛擬機運行時數據區如下圖所示:


    其中虛擬機棧是線程私有的,每個Java方法的調用對應一個棧幀在虛擬機棧中的入棧和出棧。當線程執行一個Java方法執行時,就會創建一個新的棧幀并壓入到該線程的虛擬機棧的棧頂,Java方法執行結束后棧頂的該棧幀就會彈出棧并銷毀。

    • 方法出口(返回地址)

      當一個方法被執行后,有兩種方式退出這個方法。第一種方式是執行引擎遇到任意一個方法返回的字節碼指令,這時候可能會有返回值傳遞給上層的方法調用者(調用當前方法的方法稱為調用者),是否有返回值和返回值的類型將根據遇到何種方法返回指令來決定,這種退出方法的方式稱為正常完成出口(Normal Method Invocation Completion)。

      另外一種退出方式是,在方法執行過程中遇到了異常,并且這個異常沒有在方法體內得到處理,無論是Java虛擬機內部產生的異常,還是代碼中使用athrow字節碼指令產生的異常,只要在本方法的異常表中沒有搜索到匹配的異常處理器,就會導致方法退出,這種退出方法的方式稱為異常完成出口(Abrupt Method Invocation Completion)。一個方法使用異常完成出口的方式退出,是不會給它的上層調用者產生任何返回值的。

      無論采用何種退出方式,在方法退出之后,都需要返回到方法被調用的位置,程序才能繼續執行,方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復它的上層方法的執行狀態。一般來說,方法正常退出時,調用者的PC計數器的值就可以作為返回地址,棧幀中很可能會保存這個計數器值。而方法異常退出時,返回地址是要通過異常處理器來確定的,棧幀中一般不會保存這部分信息。

      方法退出的過程實際上等同于把當前棧幀出棧,因此退出時可能執行的操作有:恢復上層方法的局部變量表和操作數棧,把返回值(如果有的話)壓入調用者棧幀的操作數棧中,調整PC計數器的值以指向方法調用指令后面的一條指令等。

    • 虛擬機棧Error

      Java虛擬機棧有可能出現的error就是StackOverflowError和OutOfMemoryError。當線程請求的棧深度大于Java虛擬機棧允許的深度時,就會拋出StackOverflowError錯誤。比如將一個方法反復遞歸,最終就會出現StackOverflowError。當Java虛擬機棧可以動態擴展時(大部分的 Java 虛擬機都可動態擴展,不過 Java 虛擬機規范中也允許固定長度的虛擬機棧),如果無法申請到足夠的內存來擴展棧,就會拋出OutOfMemoryError錯誤

    最終如果異常一直沒有處理,就會通過Thread.dispatchUncaughtException(Throwable e)進行異常分發:



    優先通過自身的uncaughtExceptionHandler處理異常,如果為null,則通過自身的ThreadGroup處理,ThreadGroup繼承UncaughtExceptionhandler,在類初始化時默認會創建兩個ThreadGroup:main、system,system是main的父ThreadGroup。


    最終異常到了system的uncaughtException(Thread t, Throwable e),在defaultUncaughtExceptionHandler中進行處理,Android中默認的是KillApplicationHandler

    /** * 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);// Don't re-enter -- avoid infinite loops if crash-reporting crashes.if (mCrashing) return;mCrashing = true;// Try to end profiling. If a profiler is running at this point, and we kill the// process (below), the in-memory buffer will be lost. So try to stop, which will// flush the buffer. (This makes method trace profiling useful to debug crashes.)if (ActivityThread.currentActivityThread() != null) {ActivityThread.currentActivityThread().stopProfiling();}// Bring up crash dialog, wait for it to be dismissedActivityManager.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);}}復制代碼

    復制代碼

    三、Android主線程異常分析

    為什么要單獨分析Android主線程異常呢?大家可能都想過一個問題,如果在uncaughtExceptinHandler中將異常攔截下來,那是不是我們的應用就永遠不會崩潰了。讀者不用再去嘗試了,筆者已經去嘗試過一次了,結果當然是不行的。作為基于事件機制的系統,從輪詢任務的過程中跳出后,其實系統就停止了。


    以上是Android 26中的ActivityThread.java源碼,看的出來這是一個進程入口,主要的是做Looper的初始化,也是App整個事件機制的開始,其中Looper.loop()就是事件輪詢的開始。

    public static void loop() {for(;;) {...Message msg = queue.next(); // might blockmsg.target.dispatchMessage(msg);...} }復制代碼

    當出現UncaughtException時,會打斷事件輪詢機制,導致App退出。

    四、總結

    本篇文章總結了Android中Java層中Crash的產生和過程,這將幫助我們去定位問題和解決問題。


    總結

    以上是生活随笔為你收集整理的Android Crash战斗日记(一、原理篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。