1. 程式人生 > >Android Studio程式碼除錯技巧篇

Android Studio程式碼除錯技巧篇

ndroid Studio目前已經成為開發Android的主要工具,用熟了可謂相當順手。作為開發者,除錯並發現bug,進而解決,可是我們的看家本領。正所謂,工欲善其事必先利其器,和其他開發工具一樣,如Eclipse、Idea,Android Studio也為我們提供了強大的除錯技巧,今天我們就來看看Android Studio中有關除錯的技巧。

首先,來看看Android studio中為我們提供的除錯面板(標準情況下): 
這裡寫圖片描述

點選右上角Restore ‘Threads’View可先展示目前相關的執行緒資訊: 
這裡寫圖片描述

android studio大體為我們提供了7個功能區:

  1. 除錯功能區
  2. 斷點管理功能區
  3. 求值表示式
  4. 執行緒幀棧區
  5. 物件變數區
  6. 變數觀察區

下面我們分別對這七個區域進行介紹。

除錯功能區

該區提供了除錯的主要操作,和你所熟知的一樣的,主要有:Step over、step into、force step into、step out、drop frame。

step over這裡寫圖片描述

單步跳過,點選該按鈕將導致程式向下執行一行。如果下一行是一個方法呼叫,那麼調式到這一行時,此行呼叫的方法已被執行完畢。比如當前程式碼是:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> num=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> min=Math.min(num,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

如果當前除錯的是第一行,當點選step over時,此時除錯的是第2行,並且Math.min(num,100)該方法已經被執行完畢。

step into這裡寫圖片描述

單步跳入,執行該操作將導致程式向下執行一行。如果下一行是自定義的方法,則進入該方法內部繼續執行,需要注意如果下一行是類庫中的方法,則不會方法內部。

force step into這裡寫圖片描述

強制單步跳入,和step into功能類似,主要區別在於:如果下一行是一個方法,則不管該方法是我們自行定義還是類庫提供的,都能跳入到方法內部繼續執行

drop frame這裡寫圖片描述

沒有好記的名字,大意理解為扔掉當前棧幀,即停止當前方法的執行,返回到當前方法被呼叫處,並且所有上下文變數的值也恢復到那個時候。簡單的舉例來說明:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> DebugDemo {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> String name = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"default"</span>;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">alertName</span>() {
        System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(name);
        debug();
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">debug</span>() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.name = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"debug"</span>;
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DebugDemo().alertName();
    }


}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul>

當你在除錯debug()時,執行該操作,將回調到debug()被呼叫的地方,也就是alertName()方法。如果此時再繼續執行drop frame,將回調到alertName()被呼叫的地方,也就是main().

求值表示式

即Evaluate expression,當執行該操作時,會在當前除錯的語句處嵌入一個互動式直譯器,在該直譯器中,你可以執行任何你想要執行的表示式進行求值操作。比如,我們在除錯時執行到以下程式碼: 
這裡寫圖片描述

此時執行Evaluate Expression,就相當於在除錯行之前嵌入了一個互動式直譯器,那麼在該直譯器中我們能做什麼呢?在這裡,我們可以對result進行求值操作:對著你想要求值得位置點選滑鼠右鍵,選擇evaluate Expression.此時會顯示如下: 
這裡寫圖片描述

在彈出的輸入框中輸入求值表示式,比如這裡我們輸入Math.min(result,50),如下圖 
這裡寫圖片描述

點選執行,我們發現在Result中已經輸出了結果,如下: 
這裡寫圖片描述

斷點管理區

執行下一個斷點這裡寫圖片描述

在很多情況下,我們會設定多個斷點以便除錯。在某些情況下,我們需要從當前斷點移動到下一個斷點處,兩個斷點之間的程式碼自動被執行,這樣我們就不需要一步一步除錯到下一個斷點了,省時又省力。舉例說明:

<code class="hljs r has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">public void test(){
    test1();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>
    test2();

}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

假設我們分別在第2行和第4行添加了斷點。如果此時我們除錯在第2行,此時點選執行該操作,當前除錯位置會自動執行到第4行,也就是第2到第4行之間的程式碼會自動被執行。

管理所有斷點這裡寫圖片描述

執行該操作將會進入斷點管理介面,在這裡你可以對目前已經新增的斷點進行管理,比如刪除,修改屬性資訊等。如圖: 
這裡寫圖片描述

修改變數值

在除錯過程中,我們可以方便的修改某個變數的值,如下: 
這裡寫圖片描述 
在上圖中,當前result的值經過計算為10,這裡我們通過Set Value將其計算結果修改為100.

停止除錯這裡寫圖片描述

所謂的停止除錯,並不是程式停止執行,而是退出除錯模式。比如除錯模式下設定瞭如下斷點: 
這裡寫圖片描述

此時如果我們執行停止操作,發現程式退出除錯模式,並正常執行完畢,Console中輸出結果:

<code class="hljs haskell has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-default" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">default</span></span>
<span class="hljs-title" style="box-sizing: border-box;">debug</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

變數觀察區

該區域將顯示你所感興趣的變數的值。在除錯模式下,你可以通過Add to Watches將某個變數新增到觀察區,該值的變化將會在變數觀察區顯示。操作如下: 
這裡寫圖片描述 
這裡我們對name比較感興趣,希望看到它的值的變化情況,因此我們將其“特殊關照”。需要注意,此時因為name是成員變數,因此在物件觀察區也可看到該值。如果是區域性變數,無疑只能用這種方式了。

斷點的分類

到目前為止,我們已經簡單的介紹了除錯功能區,斷點管理區,求值表示式,這三個區域的功能。在上面,我們不斷的提到了斷點一次,但是斷點是什麼呢?想必大部分人已經知道了,我們這裡在簡單的說明下:

斷點是偵錯程式的功能之一,可以讓程式暫停在需要的地方,幫助我們進行分析程式的執行過程。

在Android Studio中,斷點又被以下五類:

  1. 條件斷點
  2. 日誌斷點
  3. 異常斷點
  4. 方法斷點
  5. 屬性斷點

其中方法斷點是我們最熟悉的斷點型別,相信沒有人不會。下面我們著重介紹其他四種類型的斷點。

條件斷點

所謂的條件斷點就是在特定條件發生的斷點,也就是,我們可將某個斷點設定為只對某種事件感興趣,最典型的應用就是在列表迴圈中,我們希望在某特定的元素出現時暫停程式執行。比如,現在我們有個list中,其中包含了q,1q,2q,3q四個元素,我們希望在遍歷到2q時暫停程式執行,那麼需要進行如下操作: 
在需要的地方新增斷點,如下: 
這裡寫圖片描述

斷點處左鍵單擊,在Condition處填寫過濾條件.此處我們只關心2q,因此填寫s.equals("2q") 
這裡寫圖片描述

日誌斷點

該型別的斷點不會使程式停下來,而是在輸出我們要它輸出的日誌資訊,然後繼續執行。具體操作如下: 
同樣在斷點處左鍵單擊,在彈出的對話方塊中取消選中Suspend。 
這裡寫圖片描述

在彈出的控制面板中,選中Log evaluated expression,然後再填寫想要輸出的日誌資訊,如下: 
這裡寫圖片描述

當除錯過程遇到該斷點將會輸出結果,如下: 
這裡寫圖片描述

異常斷點

所謂的異常斷點就是在除錯過程中,一旦發生異常(可以指定某類異常),則會立刻定位到異常丟擲的地方。比如在除錯異常中,我們非常關注執行時異常,希望在產生任何執行異常時及時定位,那麼此時就可以利用該型別異常,在上線之前,進行異常斷點除錯非常有利於減少正式環境中發生crash的機率。 
具體操作如下:在Run選單項中,選擇View Breakpoints(也可以在斷點管理面板中點選這裡寫圖片描述),如下: 
這裡寫圖片描述

在管理斷點面板中,點選+ 
這裡寫圖片描述

在彈出的下拉選擇列表中,我們選擇Java Exception Breakpoints 
這裡寫圖片描述

這裡我們選中Search By Name,在下面的輸入框中輸入我們所關心的異常型別。此處我們關心NullPointerException,在除錯過程一旦發生NullPointerException,偵錯程式就會定位到異常發生處。 
這裡寫圖片描述

方法斷點這裡寫圖片描述

(略過吧,應該沒人不知道了)

Filed WatchPoint這裡寫圖片描述

Filed WatchPoint是本質上是一種特殊的斷點,也稱為屬性斷點:當我們某個欄位值被修改的時候,程式暫停在修改處。通常在除錯多執行緒時尤為可用,能幫我們及時的定位併發錯誤的問題。其使用和新增普通的斷點並無不同,斷點圖示稍有不同

除錯的兩種方式

到目前,除錯的相關基礎我們已經介紹完了,但是不少童鞋對Android Studio中這裡寫圖片描述這兩個按鈕感到困惑:Debug和Attach process。 
這裡我們就簡單介紹一下這兩者的區別:

  • Debug:以除錯模式安裝執行,斷點可以在執行之前設定,也可在執行後設置,是多數人最常用的調式方式
  • Attach process:和Debug方式相比,能夠將偵錯程式attach到任何正在執行的程序。比如,我們可以通過attach process到想要除錯的程序。然後,在需要的地方設定相關斷點即可。