本文轉載自:https://juejin.im/post/5a068c89f265da43052e184d
寫代碼不可避免會出現BUG,出現時就需要DEBUG。
如果看日志分析不出問題所在,可能就需要打斷點去調試。
本文通過總結Android Studio的一些調試技巧來加強我們發現並解決BUG的能力,而不是僅僅停留在“斷點單步執行”上。
一、概述
先來看一段代碼:
上圖中左側是我們打的斷點,因為斷點所在代碼類型不一樣或斷點設置不一樣,所呈現的圖標也不一樣。
在斷點位置右鍵可對該斷點進行設置,如下圖
斷點大致可分為以下幾類:
- 普通斷點
- 條件斷點
- 日志斷點
- 方法斷點
- 異常斷點
二、調試基礎
如何進入調試模式?
一般來說,下好斷點后我們有兩種方式調試一個
- Debug App:重新編譯並安裝該應用(上圖左紅圈按鈕)
- Attach Debugger to Android process:點擊后需要選擇對應的進程(上圖右紅圈按鈕)
其中第二種方式較為常用(因為不用重新進行編譯),只要運行過程中觸發到斷點就可以直接進入調試模式。
介紹下上面各個調試相關按鈕的功能:
三、條件斷點
假設你的斷點設置在一個循環列表里面,但你只對這個列表的某一個元素感興趣,希望循環到該元素時才觸發斷點。設置條件斷點也很簡單,在斷點上右鍵彈出並設置你的條件即可。
為該斷點設置的條件(假設我們預期 “i等於7時” 才觸發斷點):
- 面板左側:顯示了方法調用棧及對應信息:方法名,行號,類名和包名
- 面板右側:顯示了當前各個變量的值
四、日志斷點
很多時候,調試是為了打印日志來定位異常代碼來縮小范圍,然后再使用斷點找到問題所在。所以,經常要做的事情就是添加日志代碼,比如輸出函數參數、返回值或其他一些有用信息。
如果是通過 添加代碼 打印相關日志,就需要重新編譯整個應用,少則幾十秒多則幾分鍾。
如果是通過 日志斷點 打印相關日志,就可以完全避免編譯這些毫無意義的等待。
- 將
Suspend
屬性取消勾選(這樣雖然還叫做“斷點”,但程序並不會在該斷點斷下來) - 然后勾選
Log message to console
和Evaluate and log
(這樣就會根據你指定的表達式將信息打印到控制台)
最后,通過 Debug App 或 Attack process 方式運行程序。在 Console 面板下,不僅可以看到你打印的 斷點日志,還可以看到 正常Log類打印出來的日志。如下圖:
五、方法斷點
傳統的調試方法是以“行”為單位的,即“單步調試”。
但很多時候我們只關心某個函數的參數或返回值。
使用方法斷點,我們可以再函數級別進行調試。
設置方法斷點有兩種方式:
- 在方法行打上斷點(注意看,左邊的圖標跟普通斷點的圖標是不一樣的噢)
- 通過斷點設置窗口(View BreakPoints -> Add -> Java Method Breakpoints)
六、異常斷點
在有些情況下,我們只對某些特定的異常感興趣,而且希望程序在發生該異常時就能斷下來,就像保存現場一樣。Android Studio已經賦予了我們這個能力,即 異常斷點。
具體設置方法:打開

上圖中我們設置了關心的異常 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
被訪問或者被修改的時候觸發斷點,設置方法有兩種:
下面用一段代碼來實踐下:
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 access 和 Field modification,因此在1、2和3的位置都會觸發斷點。當在位置2觸發斷點的時候,Debug面板的顯示內容如下圖:
在上圖中我們可以看到函數的具體調用路徑,以及未執行觸發斷點代碼前所觀察變量的值。
八、Evaluate Expression
Evaluate Expression 可以直接理解為“計算表達式的值”。
這也是一個非常實用的功能,可以在斷點處直接進入一個求值環境(前面提到過該功能的按鈕圖標及含義),執行任何你感興趣的表達式或代碼片段:
九、小結
上面介紹了“各種斷點”、“變量觀察”、“表達式求值”等功能及其相關演示,實際上調試相關知識遠不止這么多。
比如,打開 View BreakPoint 設置窗口,如下圖:
我們可以對感興趣的 特定對象、特定類 進行下斷點,也可以設置 斷點次數 或設置觸發斷點的 特定線程 等。
差多不先總結到這里,想到的話再補充。
作者:zhuanghongji
鏈接:https://juejin.im/post/5a068c89f265da43052e184d
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。