調(diào)試
這是 “使用 Android Studio 開(kāi)發(fā) Web 程序” 系列的第三篇文章植锉,接續(xù)前一篇文章的內(nèi)容页衙,源代碼照著規(guī)格打完了区转,第一件事當(dāng)然就是先運(yùn)行看看成果,如果有出現(xiàn)不符預(yù)期的結(jié)果瞻润,就要進(jìn)到了調(diào)試的程序以便修正源代碼喘垂。但如果負(fù)責(zé)的是 Server 端的元件,還要先準(zhǔn)備好 Container 的環(huán)境绍撞、布署編譯好的檔案正勒、調(diào)整設(shè)定檔等等的程序。過(guò)往大部份人可能都會(huì)選擇 Tomcat 做為 Container傻铣,而現(xiàn)在有一個(gè)輕量化的選擇 Jetty章贞。
Jetty 已在 Gradle 的支持范圍內(nèi),透過(guò) Gradle 的 Jetty Plugin 來(lái)運(yùn)行 jettyRun Task 就可以直接啟動(dòng) Jetty矾柜,并透過(guò)瀏覽器來(lái)檢視程序運(yùn)行后的網(wǎng)頁(yè)結(jié)果阱驾,不需要再加外進(jìn)行安裝 Jetty 的動(dòng)作。
使用 Jetty Plugin 的 build.gradle 檔案內(nèi)容怪蔑,示范如下:
apply plugin: "war"
apply plugin: "jetty"
dependencies {
compile "javax.servlet:servlet-api:2.5"
}
httpPort = 8080
stopPort = 9090
stopKey = "stopKey"
以下是使用 Android Studio 的 Terminal 窗口下指令后顯示的結(jié)果(運(yùn)行前要先切換到項(xiàng)目所在的文件夾):
如果運(yùn)行成功里覆,就可以依照結(jié)果上出現(xiàn)的網(wǎng)址輸入在瀏覽器里,瀏覽器會(huì)顯示程序運(yùn)行的結(jié)果缆瓣。另外喧枷,由于 Android Studio 產(chǎn)生的 Gradle 項(xiàng)目預(yù)設(shè)會(huì)使用 Gradle Wrapper,Wrapper 會(huì)被產(chǎn)生在項(xiàng)目的相同文件夾下弓坞。運(yùn)行時(shí)是使用名稱為 gradlew 的 Wrapper隧甚,在 Windows 的平臺(tái)是批文件、非 Windows 的平臺(tái)則為 Shell Script渡冻,<u>Shell Script 要先設(shè)定運(yùn)行的權(quán)限</u>戚扳。透過(guò) Wrapper 會(huì)自動(dòng)下載 Gradle 的程序后運(yùn)行,所以不用先行安裝 Gradle族吻,但是如果對(duì) Gradle 版本有特別要求的話帽借,則要改為使用自行安裝 Gradle 的方式珠增。
只是要在 IDE 中設(shè)斷點(diǎn),并且在瀏覽器提出要求的過(guò)程中觸發(fā)中斷砍艾,還需要在命令列中增加以下的參數(shù):
—Dorg.gradle.daemon=true -Dorg.gradle.jvmargs="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
第一個(gè)參數(shù)是要 Gradle 以 Daemon 模式運(yùn)作蒂教,Daemon 模式下會(huì)重用 Process、減少 Gradle 程序啟動(dòng)的成本脆荷,可以加快每一次運(yùn)行 Gradle 指令時(shí)的生成速度凝垛。當(dāng)只有第二個(gè)參數(shù)被使用時(shí),第一個(gè)參數(shù)是輸出時(shí)會(huì)提示的選項(xiàng)蜓谋,同時(shí)也是官網(wǎng)建議在進(jìn)行開(kāi)發(fā)梦皮、調(diào)試時(shí)使用的選項(xiàng)。如果是使用 Android Studio 來(lái)啟動(dòng) Task 則不用特別設(shè)定孤澎,預(yù)設(shè)就是使用 Daemon 模式届氢。
第二個(gè)參數(shù)是開(kāi)啟 JVM 調(diào)試功能欠窒,讓 Debugger 可以透過(guò) 5005 Port
與 JVM 建立調(diào)試所需的通訊機(jī)制覆旭。address=5005
所指定的數(shù)字可以更改成任意沒(méi)有被占用的 Port,但必須要與 Debugger 所設(shè)定的 Port 相同岖妄。
完成了準(zhǔn)備的工作后型将,接下來(lái)就要讓 Android Studio 的 Debugger 知道調(diào)試目標(biāo)的資訊以便進(jìn)行調(diào)試的工作。點(diǎn)選 Android Studio 的選單功能【Run -> Edit Configurations...】會(huì)出現(xiàn) “Run/Debug Configurations” 窗口荐虐。
在 “Run/Debug Configurations” 窗口左上方點(diǎn)選【+】按鈕七兜,選擇 “Remote” 類型,在下方清單的 Remote 分類就會(huì)多出一個(gè)項(xiàng)目福扬,設(shè)定的畫(huà)面如下方所示腕铸。
所輸入的 “Name” 會(huì)被顯示在 Toolbar 中【Run/Debug Configuration】的下拉菜單中。由于同時(shí)啟動(dòng)多個(gè) Configuration 的運(yùn)行個(gè)體會(huì)有 Port 被占用的問(wèn)題铛碑,所以勾選了 “Single instance only” 選項(xiàng)狠裹。
在 Configuration 選項(xiàng)卡中,上半部是提示要設(shè)定 JVM 進(jìn)行遠(yuǎn)端調(diào)試的參數(shù)范例汽烦,不同時(shí)期的 JDK 有不同的參數(shù)規(guī)格涛菠,每一個(gè)范例右方有復(fù)制按鈕,可以直接復(fù)制到剪貼簿中撇吞,省去打字的麻煩俗冻。
“Settings” 群組內(nèi)的選項(xiàng),如果是在開(kāi)發(fā)階段于本機(jī)進(jìn)行調(diào)試可以使用預(yù)設(shè)的內(nèi)容牍颈。但如果是在系統(tǒng)測(cè)試或正式環(huán)境中調(diào)試迄薄,Host 值應(yīng)該要依據(jù)實(shí)際的 Server 名稱或 IP 位址填入。Port 則是依據(jù) JVM 參數(shù)所下的 address 數(shù)值做調(diào)整煮岁。
完成以上的設(shè)定讥蔽,就可以在 Terminal 窗口中運(yùn)行之前示范并加上參數(shù)的指令死姚,待等 Jetty 啟動(dòng)∏诶海回到源代碼編輯窗口設(shè)定好正確的斷點(diǎn)位置都毒,接著在 Toolbar 中,選擇【Run/Debug Configuration】的下拉清單里剛才設(shè)定的 Remote 類型的項(xiàng)目碰缔,按下右方 Debug 圖標(biāo)的按鈕或 Shift+F9 的快速鍵账劲。
如果一切順利,會(huì)在 Android Studio 下方的 Console 窗口中看到以下訊息:
Connected to the target VM, address: 'localhost:5005', transport: 'socket'
代表 Debugger 已經(jīng)成功的和 Jetty 的 JVM 連結(jié)上金抡,可以開(kāi)始進(jìn)行調(diào)試的作業(yè)瀑焦。此時(shí)就可以啟動(dòng)瀏覽器,輸入網(wǎng)址梗肝,以便運(yùn)行程序榛瓮。當(dāng)運(yùn)行到所設(shè)定的斷點(diǎn)就會(huì)和其他項(xiàng)目一樣,IDE 就會(huì)停在斷點(diǎn)對(duì)應(yīng)的源代碼上巫击,并且可進(jìn)行觀察變量?jī)?nèi)容禀晓、單步執(zhí)行等操作。
到這里雖然工作已經(jīng)可以進(jìn)行了坝锰,但是每一次運(yùn)行調(diào)試都要先下一次指令太麻煩了粹懒,而且當(dāng)源代碼有修改,則 Jetty 要重啟才會(huì)運(yùn)行新的內(nèi)容顷级。然而先前所下的 gradlew 指令會(huì) Block 住 Terminal凫乖,要先按下 Ctrl+C 之后才會(huì)關(guān)閉 Jetty,也才能再下一次啟動(dòng) Jetty 的指令弓颈。
gradlew 指令會(huì) Block 住的情況如果不消除帽芽,對(duì)以后自動(dòng)化生成的作業(yè)也會(huì)造成問(wèn)題、導(dǎo)致自動(dòng)化無(wú)法進(jìn)行翔冀。所幸 Jetty Plugin 也有提供 Daemon 參數(shù)导街,可以消除 jettyRun 運(yùn)行后會(huì) Block 住的情況。為了后續(xù)自動(dòng)化生成也可以一體套用橘蜜,所以在項(xiàng)目文件夾的 build.gradle 里加上以下的內(nèi)容:
jettyRun.daemon = true
加完了之后菊匿,可以先在 Terminal 試看看,果然可以直接回到提示字元计福。但這時(shí)要記得再運(yùn)行 jettyStop 的 Task跌捆,不然 Jetty 沒(méi)有關(guān)閉,下次運(yùn)行 jettyRun 時(shí)會(huì)出現(xiàn) Port 被占用的錯(cuò)誤訊息象颖。
接下來(lái)為了確保 jettyRun 在運(yùn)行前佩厚,Jetty 都有確實(shí)做關(guān)閉的程序,所以要在 build.gradle 里設(shè)定 jettyRun 運(yùn)行之前要先運(yùn)行 jettyStop说订。但目前示范使用的 Gradle 2.2.1 有一個(gè) Bug抄瓦,使用 Daemon 模式的 Jetty 無(wú)法被 jettyStop 正常地關(guān)閉潮瓶,參考官方的解決方法后,必須要新增的內(nèi)容如下:
import org.gradle.api.plugins.jetty.internal.Monitor
[jettyRun, jettyRunWar]*.doLast {
/**
* THIS IS A WORKAROUND! THE CURRENT VERSION OF THIS TASK DOESN'T START A WATCHER IN DAEMON MODE
*
* If starting the monitor fails, it may be because the jetty task was updated to fix this issue
* When that happens, we shouldn't need the custom task any more
*
* Copied From: AbstractJettyRunTask
*/
if (getStopPort() != null && getStopPort() > 0 && getStopKey() != null) {
Monitor monitor = new Monitor(getStopPort(), getStopKey(), server.getProxiedObject());
monitor.start();
}
}
[jettyRun, jettyRunWar].each { jetty ->
jetty.dependsOn jettyStop
}
剩下的就是讓之前設(shè)定好的 Run/Debug Configuration 與 jettyRun 串連起來(lái)钙姊,先回到 “Run/Debug Configurations” 窗口毯辅,并新增一個(gè) Gradle 類型的 Run/Debug Configuration。在 “Name” 的欄位里輸入一個(gè)自己可以識(shí)別的名稱煞额,同時(shí)為了保險(xiǎn)起見(jiàn)勾選了 “Single instance only” 選項(xiàng)思恐,以避免 Debug Port 被不同 JVM Instance 占住了。
“Gradle project” 欄位直接由 Registered Gradle projects 里挑選對(duì)應(yīng)的 Web 項(xiàng)目膊毁,“Tasks” 欄位輸入 jettyRun
胀莹,“VM Option”輸入 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50005
,或是由 Remote 類型中復(fù)制后貼上婚温。
這里補(bǔ)充說(shuō)明一下描焰,JVM 的參數(shù)也可以設(shè)定在 gradle.property 里,gradle.property 可放在以下幾個(gè)位置:
- 要套用 JVM 參數(shù)的項(xiàng)目其 build.gradle 所在文件夾
- 登錄使用者 Home 路徑下的 .gradle 文件夾
但在命令列中指定栅螟,將會(huì)覆蓋過(guò) gradle.property 設(shè)定的內(nèi)容荆秦,先前的 VM Option 則等同于在命令列中指定。選擇在 Run/Debug Configuration 里設(shè)定是因?yàn)橹挥性?IDE 里才有 Debug 的需求嵌巷,如果設(shè)在項(xiàng)目文件夾的 gradle.property 檔案內(nèi)萄凤,當(dāng)檔案被 Checkin 到版控系統(tǒng),可能會(huì)被 CI 系統(tǒng)下載進(jìn)而影響測(cè)試或自動(dòng)化生成的結(jié)果搪哪。所以為了不影響測(cè)試或自動(dòng)化生成,才會(huì)避免在 gradle.property 中進(jìn)行相關(guān)的設(shè)定坪圾。
最后晓折,點(diǎn)選回到最早建立的 Remote 類型項(xiàng)目,在設(shè)定畫(huà)面的右下方空白清單中指定剛才新增 Gradle 類型項(xiàng)目為 Before execute兽泄。完成后漓概,在【Run/Debug Configuration】的下拉清單指定 Remote 類型項(xiàng)目并點(diǎn)選 Toolbar 上的 Debug 圖標(biāo),如果設(shè)定無(wú)誤就可以看到過(guò)程中運(yùn)行了 jettyStop 及 jettyRun病梢,并把 Debugger 連結(jié)到指定的 Port 上一次完成胃珍。所以每次重啟調(diào)試只要一個(gè)按鍵,再切換回瀏覽器重新載入頁(yè)面即可蜓陌。
如果不是要調(diào)試觅彰,只是要看運(yùn)行的結(jié)果,可以將【Run/Debug Configuration】的下拉清單切換成 Gradle 類型的項(xiàng)目后钮热,按下運(yùn)行圖標(biāo)的按鈕即可填抬。
在 GitHub 上有人提供了一個(gè)可以使程序更簡(jiǎn)易的 Plugin 叫 Gretty,有興趣可以自行研究看看隧期。接下來(lái)會(huì)進(jìn)入到系列文章的最后一篇:測(cè)試飒责。
以下為完整的 build.gradle 的內(nèi)容:
apply plugin: "war"
apply plugin: "jetty"
import org.gradle.api.plugins.jetty.internal.Monitor
[jettyRun, jettyRunWar]*.doLast {
/**
* THIS IS A WORKAROUND! THE CURRENT VERSION OF THIS TASK DOESN'T START A WATCHER IN DAEMON MODE
*
* If starting the monitor fails, it may be because the jetty task was updated to fix this issue
* When that happens, we shouldn't need the custom task any more
*
* Copied From: AbstractJettyRunTask
*/
if (getStopPort() != null && getStopPort() > 0 && getStopKey() != null) {
Monitor monitor = new Monitor(getStopPort(), getStopKey(), server.getProxiedObject());
monitor.start();
}
}
[jettyRun, jettyRunWar].each { jetty ->
jetty.dependsOn jettyStop
}
dependencies {
compile project(":utils")
compile "javax.servlet:servlet-api:2.5"
}
jettyRun.daemon = true
httpPort = 8080
stopPort = 9090
stopKey = "stopKey"