偵錯
這是「使用 Android Studio 開發 Web 程式」系列的第三篇文章,接續前一篇文章的內容,程式碼照著規格打完了,第一件事當然就是先執行看看成果,如果有出現不符預期的結果,就要進到了偵錯的程序以便修正程式碼。但如果負責的是 Server 端的元件,還要先準備好 Container 的環境、佈署編譯好的檔案、調整設定檔等等的程序。過往大部份人可能都會選擇 Tomcat 做為 Container,而現在有一個輕量化的選擇 Jetty。Jetty 已在 Gradle 的支援範圍內,透過 Gradle 的 Jetty Plugin 來執行 jettyRun Task 就可以直接啟動 Jetty,並透過瀏覽器來檢視程式執行後的網頁結果,不需要再加外進行安裝 Jetty 的動作。
使用 Jetty Plugin 的 build.gradle 檔案內容,示範如下:
以下是使用 Android Studio 的 Terminal 視窗下指令後顯示的結果(執行前要先切換到專案所在的目錄):
如果執行成功,就可以依照結果上出現的網址輸入在瀏覽器裡,瀏覽器會顯示程式執行的結果。另外,由於 Android Studio 產生的 Gradle 專案預設會使用 Gradle Wrapper,Wrapper 會被產生在專案的相同目錄下。執行時是使用名稱為 gradlew 的 Wrapper,在 Windows 的平台是批次檔、非 Windows 的平台則為 Shell Script,Shell Script 要先設定執行的權限。透過 Wrapper 會自動下載 Gradle 的程式後執行,所以不用先行安裝 Gradle,但是如果對 Gradle 版本有特別要求的話,則要改為使用自行安裝 Gradle 的方式。
只是要在 IDE 中設中斷點,並且在瀏覽器提出要求的過程中觸發中斷,還需要在命令列中增加以下的參數:
—Dorg.gradle.daemon=true -Dorg.gradle.jvmargs="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
第一個參數是要 Gradle 以 Daemon 模式運作,Daemon 模式下會重用 Process、減少 Gradle 程式啟動的成本,可以加快每一次執行 Gradle 指令時的建置速度。當只有第二個參數被使用時,第一個參數是輸出時會提示的選項,同時也是官網建議在進行開發、偵錯時使用的選項。如果是使用 Android Studio 來啟動 Task 則不用特別設定,預設就是使用 Daemon 模式。
第二個參數是開啟 JVM 偵錯功能,讓 Debugger 可以透過 5005 Port 與 JVM 建立偵錯所需的通訊機制。"address=5005" 所指定的數字可以更改成任意沒有被佔用的 Port,但必須要與 Debugger 所設定的 Port 相同。
完成了準備的工作後,接下來就要讓 Android Studio 的 Debugger 知道偵錯目標的資訊以便進行偵錯的工作。點選 Android Studio 的選單功能【Run -> Edit Configurations...】會出現「Run/Debug Configurations」視窗。
在「Run/Debug Configurations」視窗左上方點選【+】按鈕,選擇「Remote」類型,在下方清單的 Remote 分類就會多出一個項目,設定的畫面如下方所示。
所輸入的「Name」會被顯示在 Toolbar 中【Run/Debug Configuration】的下拉清單中。由於同時啟動多個 Configuration 的執行個體會有 Port 被佔用的問題,所以勾選了「Single instance only」選項。
在 Configuration 頁籤中,上半部是提示要設定 JVM 進行遠端偵錯的參數範例,不同時期的 JDK 有不同的參數規格,每一個範例右方有複製按鈕,可以直接複製到剪貼簿中,省去打字的麻煩。
「Settings」群組內的選項,如果是在開發階段於本機進行偵錯可以使用預設的內容。但如果是在系統測試或正式環境中偵錯,Host 值應該要依據實際的 Server 名稱或 IP 位址填入。Port 則是依據 JVM 參數所下的 address 數值做調整。
完成以上的設定,就可以在 Terminal 視窗中執行之前示範並加上參數的指令,待等 Jetty 啟動。回到程式碼編輯視窗設定好正確的中斷點位置,接著在 Toolbar 中,選擇【Run/Debug Configuration】的下拉清單裡剛才設定的 Remote 類型的項目,按下右方 Debug 圖示的按鈕或 Shift+F9 的快速鍵。
如果一切順利,會在 Android Studio 下方的 Console 視窗中看到以下訊息:
Connected to the target VM, address: 'localhost:5005', transport: 'socket'
代表 Debugger 已經成功的和 Jetty 的 JVM 連結上,可以開始進行偵錯的作業。此時就可以啟動瀏覽器,輸入網址,以便執行程式。當執行到所設定的中斷點就會和其他專案一樣,IDE 就會停在中斷點對應的程式碼上,並且可進行觀察變數內容、單步執行等操作。
到這裡雖然工作已經可以進行了,但是每一次執行偵錯都要先下一次指令太麻煩了,而且當程式碼有修改,則 Jetty 要重啟才會執行新的內容。然而先前所下的 gradlew 指令會 Block 住 Terminal,要先按下 Ctrl+C 之後才會關閉 Jetty,也才能再下一次啟動 Jetty 的指令。
gradlew 指令會 Block 住的情況如果不消除,對以後自動化建置的作業也會造成問題、導致自動化無法進行。所幸 Jetty Plugin 也有提供 Daemon 參數,可以消除 jettyRun 執行後會 Block 住的情況。為了後續自動化建置也可以一體套用,所以在專案目錄的 build.gradle 裡加上以下的內容:
加完了之後,可以先在 Terminal 試看看,果然可以直接回到提示字元。但這時要記得再執行 jettyStop 的 Task,不然 Jetty 沒有關閉,下次執行 jettyRun 時會出現 Port 被佔用的錯誤訊息。
接下來為了確保 jettyRun 在執行前,Jetty 都有確實做關閉的程序,所以要在 build.gradle 裡設定 jettyRun 執行之前要先執行 jettyStop。但目前示範使用的 Gradle 2.2.1 有一個 Bug,使用 Daemon 模式的 Jetty 無法被 jettyStop 正常地關閉,參考官方的解決方法後,必須要新增的內容如下:
剩下的就是讓之前設定好的 Run/Debug Configuration 與 jettyRun 串連起來,先回到「Run/Debug Configurations」視窗,並新增一個 Gradle 類型的 Run/Debug Configuration。在「Name」的欄位裡輸入一個自己可以識別的名稱,同時為了保險起見勾選了「Single instance only」選項,以避免 Debug Port 被不同 JVM Instance 佔住了。
「Gradle project」欄位直接由 Registered Gradle projects 裡挑選對應的 Web 專案,「Tasks」欄位輸入 jettyRun,「VM Option」輸入 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50005,或是由 Remote 類型中複製後貼上。
這裡補充說明一下,JVM 的參數也可以設定在 gradle.property 裡,gradle.property 可放在以下幾個位置:
- 要套用 JVM 參數的專案其 build.gradle 所在目錄
- 登入使用者 Home 路徑下的 .gradle 目錄
但在命令列中指定,將會覆蓋過 gradle.property 設定的內容,先前的 VM Option 則等同於在命令列中指定。選擇在 Run/Debug Configuration 裡設定是因為只有在 IDE 裡才有 Debug 的需求,如果設在專案目錄的 gradle.property 檔案內,當檔案被 Checkin 到版控系統,可能會被 CI 系統下載進而影響測試或自動化建置的結果。所以為了不影響測試或自動化建置,才會避免在 gradle.property 中進行相關的設定。
最後,點選回到最早建立的 Remote 類型項目,在設定畫面的右下方空白清單中指定剛才新增 Gradle 類型項目為 Before execute。完成後,在【Run/Debug Configuration】的下拉清單指定 Remote 類型項目並點選 Toolbar 上的 Debug 圖示,如果設定無誤就可以看到過程中執行了 jettyStop 及 jettyRun,並把 Debugger 連結到指定的 Port 上一次完成。所以每次重啟偵錯只要一個按鍵,再切換回瀏覽器重新載入頁面即可。
如果不是要偵錯,只是要看執行的結果,可以將【Run/Debug Configuration】的下拉清單切換成 Gradle 類型的項目後,按下執行圖示的按鈕即可。
在 GitHub 上有人提供了一個可以使程序更簡易的 Plugin 叫 Gretty,有興趣可以自行研究看看。接下來會進入到系列文章的最後一篇:測試。
以下為完整的 build.gradle 的內容:
0 意見:
張貼留言