Android Studio 中的除錯技巧

zhuanghongji發表於2017-11-12


原文地址: github.com/zhuanghongj… 

寫程式碼不可避免會出現BUG,出現時就需要DEBUG。

如果看日誌分析不出問題所在,可能就需要打斷點去除錯。
本文通過總結Android Studio的一些除錯技巧來加強我們發現並解決BUG的能力,而不是僅僅停留在“斷點單步執行”上。

一、概述

先來看一段程式碼:

上圖中左側是我們打的斷點,因為斷點所在程式碼型別不一樣或斷點設定不一樣,所呈現的圖示也不一樣。
在斷點位置右鍵可對該斷點進行設定,如下圖

  • 變數(相關設定視窗)

  • 方法(相關設定視窗)

  • 普通程式碼(相關設定視窗)

斷點大致可分為以下幾類:

  • 普通斷點
  • 條件斷點
  • 日誌斷點
  • 方法斷點
  • 異常斷點

二、除錯基礎

如何進入除錯模式?


一般來說,下好斷點後我們有兩種方式除錯一個

Debuggable
Apk

  • Debug App:重新編譯並安裝該應用(上圖左紅圈按鈕)
  • Attach Debugger to Android process:點選後需要選擇對應的程式(上圖右紅圈按鈕)

其中第二種方式較為常用(因為不用重新進行編譯),只要執行過程中觸發到斷點就可以直接進入除錯模式。

介紹下上面各個除錯相關按鈕的功能:

圖示名稱功能描述
Resume Program當你執行到某個斷點的時候,點選之後會繼續程式的執行
(後面如果有斷點的話會暫停在該斷點出)
Pause Program暫停執行
Stop App停止程式
View Breakpoints檢視所有斷點
Mute Breakpoints沉默所有斷點
(選中後所有斷點圖示的主色調都會程式設計灰白色,並且不會觸發任何斷點)
Get Thread Dump顯示執行緒相關資訊
Restore LayoutTODO
Settings設定
Show Excution PointsTODO
Step Over單步執行,可以簡單理解為“執行到下一行程式碼”
Step Into進入當前方法內部
Force Step IntoTODO
Step Out跳出當前方法
Drop FrameTODO
Run To Cursor直接執行到“浮標”所在的那行程式碼
Evaluate Expression進行表示式求值

三、條件斷點

假設你的斷點設定在一個迴圈列表裡面,但你只對這個列表的某一個元素感興趣,希望迴圈到該元素時才觸發斷點。設定條件斷點也很簡單,在斷點上右鍵彈出並設定你的條件即可

先看一個打了條件斷點的程式碼:

為該斷點設定的條件(假設我們預期 “i等於7時” 才觸發斷點):

觸發斷點後,檢視Debugger皮膚:

  • 皮膚左側:顯示了方法呼叫棧及對應資訊:方法名,行號,類名和包名
  • 皮膚右側:顯示了當前各個變數的值

四、日誌斷點

很多時候,除錯是為了列印日誌來定位異常程式碼來縮小範圍,然後再使用斷點找到問題所在。所以,經常要做的事情就是新增日誌程式碼,比如輸出函式引數、返回值或其他一些有用資訊。

如果是通過 新增程式碼 列印相關日誌,就需要重新編譯整個應用,少則幾十秒多則幾分鐘。
如果是通過 日誌斷點 列印相關日誌,就可以完全避免編譯這些毫無意義的等待。

再來看一段程式碼:

在想要列印日誌的地方下斷點,然後右鍵該斷點並進行相關設定:

  • Suspend 屬性取消勾選(這樣雖然還叫做“斷點”,但程式並不會在該斷點斷下來)
  • 然後勾選 Log message to consoleEvaluate and log(這樣就會根據你指定的表示式將資訊列印到控制檯)

最後,通過 Debug AppAttack process 方式執行程式。在 Console 皮膚下,不僅可以看到你列印的 斷點日誌,還可以看到 正常Log類列印出來的日誌。如下圖:

五、方法斷點

傳統的除錯方法是以“行”為單位的,即“單步除錯”。
但很多時候我們只關心某個函式的引數或返回值。
使用方法斷點,我們可以再函式級別進行除錯。

設定方法斷點有兩種方式:

  • 在方法行打上斷點(注意看,左邊的圖示跟普通斷點的圖示是不一樣的噢)
  • 通過斷點設定視窗(View BreakPoints -> Add -> Java Method Breakpoints)

六、異常斷點

在有些情況下,我們只對某些特定的異常感興趣,而且希望程式在發生該異常時就能斷下來,就像儲存現場一樣。Android Studio已經賦予了我們這個能力,即 異常斷點

具體設定方法:開啟

“View Breakpoints”
,點選加號並新增你感興趣的異常

上圖中我們設定了關心的異常 IndexOutOfBoundsException,下面我們寫一段測試程式碼:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] strs = new String[]{"0", "1"};
        Log.w("BP", strs[2]);  // Index out of bounds
    }
}複製程式碼

上面這段測試程式碼,如果是直接編譯執行的話會導致App閃退。而如果是通過除錯模式執行的話則會觸發 Java Exception Breakpoints,程式碼編輯器會直接顯示觸發斷點的程式碼,並在 Debug 皮膚上顯示相關資訊,如下圖:
注:如果此時你觸發的是一個NullPointerException,則不會觸發異常斷點(因為還沒有新增到“感興趣”列表中)

有同學會想,如果我捕捉了異常還會觸發異常斷點嗎?
答:即使進行了 try...catch... 捕捉異常,斷點依然會在 catch 之前觸發

還有同學會想,如果我對所有的異常或未知的異常感興趣呢?
答:目前我也沒找到好解決辦法,試了“勾選 Any Exception”、“新增 Exception”、“新增 UndeclaredThrowableException” 這幾種方法,都未能快速定位到異常程式碼,知道的同學可以PR下。

七、Field WatchPoint

前面我們新增“異常斷點”並且點選“加號”後,顯示的第二個項 Java Field Watchpoints 是幹什麼的呢?
有木有這樣一種場景:某個變數的值莫名奇妙地不知道被誰修改了?

Java雖然是值傳遞,但引用也可以是值。所有的物件都存放在堆上面,而堆是被所有執行緒共享的。因此,在複雜情況下,你根本不知道這些共享變數是被誰修改了,也不知道具體的函式呼叫路徑。哥,這很危險。

在多執行緒環境下,不變性是一個很重要的特性,高併發性語言(如 Erlang、Scala 等)都對這種不變性有著一定程度的支援。

廢話了這麼多,現在進入正題。
Field WatchPoint 就是我們解決上面難題的關鍵所在,使用它使得我們可以在某個 Field 被訪問或者被修改的時候觸發斷點,設定方法有兩種:

  • 第一種:直接在某個變數的宣告處下斷點(此時斷點圖示和普通斷點圖示也是不一樣的)
    右鍵該斷點可以進行設定

  • 第二種:在 View BreakPoints 中進行設定,直接指定某個類的某個變數

下面用一段程式碼來實踐下:

public class MainActivity extends AppCompatActivity {

    private String mField;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mField = "ABC";         // 1. 修改值
        changeFieldMethod1();
    }

    private void changeFieldMethod1() {
        changeFieldMethod2();
    }

    private void changeFieldMethod2() {
        mField = "Android";     // 2. 修改值
        Log.i("BP", "mField = " + mField);   // 3. 訪問值
    }
}複製程式碼

我們對 mField 設定了 Field WatchPoint 並同時勾選了 Field accessField modification,因此在1、2和3的位置都會觸發斷點。當在位置2觸發斷點的時候,Debug皮膚的顯示內容如下圖:

在上圖中我們可以看到函式的具體呼叫路徑,以及未執行觸發斷點程式碼前所觀察變數的值。

八、Evaluate Expression

Evaluate Expression 可以直接理解為“計算表示式的值”。
這也是一個非常實用的功能,可以在斷點處直接進入一個求值環境(前面提到過該功能的按鈕圖示及含義),執行任何你感興趣的表示式或程式碼片段:

  • 表示式求值
  • 程式碼片段求值

九、小結

上面介紹了“各種斷點”、“變數觀察”、“表示式求值”等功能及其相關演示,實際上除錯相關知識遠不止這麼多。
比如,開啟 View BreakPoint 設定視窗,如下圖:
我們可以對感興趣的 特定物件特定類 進行下斷點,也可以設定 斷點次數 或設定觸發斷點的 特定執行緒 等。

差多不先總結到這裡,想到的話再補充。

參考文章:


相關文章