注:這是一篇翻譯文章抱环,原文:How to improve quality and syntax of your Android code绊起,為了理解連貫写半,翻譯過(guò)程中我修改了一些陳述邏輯和順序吏恭,同時(shí)也加了一些自己的補(bǔ)充惠豺。
在這片文章中辽故,我將從工具使用的角度上講述如何提高 Android 代碼質(zhì)量徒仓,這些自動(dòng)化工具包括 Checksytle、Findbugs誊垢、PMD 和 Android Lint. 團(tuán)隊(duì)中代碼意識(shí)不一致掉弛,水平參差不齊,代碼風(fēng)格迥異喂走,定下的規(guī)范也是熟視無(wú)睹殃饿。這時(shí)候就需要借助工具的力量,利用工具自動(dòng)地幫助我們檢測(cè)代碼芋肠,避免代碼惡習(xí)乎芳,預(yù)防蟻穴壞堤。
0.1 Fork 這個(gè)例子工程
我強(qiáng)烈建議你fork這個(gè)例子工程帖池,所有的使用事列都會(huì)在這個(gè)demo中呈現(xiàn)奈惑,同時(shí)你可以測(cè)試你自己寫的規(guī)則。
0.2 關(guān)于 Gradle 的 Task
理解 Gradle 的 Task 是理解這篇文章的基礎(chǔ)睡汹,我強(qiáng)烈建議你多看看關(guān)于Gradle Task的文章(例如這篇還有這篇)肴甸,當(dāng)然,本文也是滿滿的例子囚巴,所以你很容易理解原在,這也是我建議你 fork 我的代碼倉(cāng)庫(kù)的原因,把工程導(dǎo)入你的 Android studio彤叉,然后你將看到熟悉的 Gradle Task 腳本晤斩。如果你還是不太理解,也不用擔(dān)心姆坚,我將最大努力的寫好注釋澳泵。
0.3 關(guān)于 這個(gè) demo 的層級(jí)結(jié)構(gòu)
Gradle 腳本可以分散在不同的文件中,我在工程中寫了三個(gè) gradle 文件:
- 根目錄下的 gradle 文件兼呵,用來(lái)配置工程相關(guān)的信息兔辅,例如倉(cāng)庫(kù)依賴腊敲,編譯工具版本之類的。
- app 目錄下的 gradle 文件维苔,用來(lái)校驗(yàn) Java 類碰辅。
- config 目錄下的 gradle 文件,用來(lái)配置了以上提到的幾個(gè)工具介时。
1 Checkstyle
Check style 是一個(gè)幫助開發(fā)者維持編碼規(guī)范標(biāo)準(zhǔn)的一個(gè)工具没宾,它能自動(dòng)地檢測(cè) Java 代碼,以減少人工檢測(cè)代碼的成本沸柔。當(dāng)你啟用 Checkstyle循衰,它能解析你的代碼并能告訴你代碼中的錯(cuò)誤或者不符合定義的規(guī)范的地方。
1.1 Android Studio 插件
Checkstyle 提供了多種IDE的插件支持褐澎,Android Studio 也不例外会钝。
進(jìn)入Android Studio設(shè)置頁(yè)面,在插件欄輸入Checkstyle:
如果你還沒(méi)安裝 Checkstyle 的插件工三,進(jìn)入下載頁(yè)下載迁酸,然后重啟 Android Studio。啟動(dòng) Android studio 后進(jìn)入 Checkstyle 的設(shè)置頁(yè)面:
在這里我們可以設(shè)置 Checkstyle 插件掃描范圍俭正,配置文件等信息奸鬓,默認(rèn)使用的配置文件是官方提供的文件:sun_checks.xml,我們也可以根據(jù)自己項(xiàng)目的需要定義自己的配置文件掸读,配置規(guī)則可以參考官方文檔全蝶。
設(shè)置完成之后點(diǎn)擊 “Apply” 或者 “ok” 按鈕,回到代碼中寺枉,我們便可以看到 Checkstyle 給我們的提示:
1.2 Gradle 方式使用Checkstyle
如果我們需要把 Checkstyle 繼承到自動(dòng)編譯服務(wù)器中抑淫,例如:jenkins,我們需要利用 Gradle Task來(lái)執(zhí)行 Checkstyle,下面這段腳本展示了在 Gradle task 的 Checkstyle 的基本配置:
task checkstyle(type: Checkstyle) {
configFile file("${project.rootDir}/config/quality/checkstyle/checkstyle.xml") // Where my checkstyle config is...
configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath // Where is my suppressions file for checkstyle is...
source 'src'
include '**/*.java'
exclude '**/gen/**'
classpath = files()
}
這個(gè) task 將會(huì)根據(jù)你指定的 checkstyle.xml 和 suppressions.xml 文件來(lái)分析你的代碼姥闪。你可以在 Android Studio 中執(zhí)行這個(gè)任務(wù):
執(zhí)行完成之后始苇,checkstyle 工具將會(huì)把每一個(gè)不合法的問(wèn)題顯示在控制臺(tái)中。
2 FindBugs
FindBugs 這個(gè)名字本身已經(jīng)揭示了它的作用筐喳,“FindBugs uses static analysis to inspect Java bytecode for occurrences of bug patterns.” FindBugs 是一個(gè)工具催式,它能通過(guò)靜態(tài)分析方式掃描 Java 字節(jié)碼,發(fā)現(xiàn)其中的可能出現(xiàn) bug 的代碼避归,它能發(fā)現(xiàn)一些常規(guī)的低級(jí)的錯(cuò)誤荣月,例如一些錯(cuò)誤的邏輯操作,也能發(fā)現(xiàn)一些比較隱晦的錯(cuò)誤梳毙。
例如:
Person person = (Person) map.get("bob");
if (person != null) {
person.updateAccessTime();
}
String name = person.getName();
最后一行代碼哺窄,可能會(huì)出現(xiàn)空指針錯(cuò)誤。
又如:
b.replace('b', 'p');
if(b.equals("pop")) {
Log.d("","");
}
b.replace('b', ‘p’);這段代碼對(duì)b不會(huì)產(chǎn)生影響,所以是無(wú)效的萌业。
2.1 Android Studio 插件
同樣坷襟,F(xiàn)Indbugs 也提供了 Android Studio 的插件支持,插件的獲取過(guò)程和 Checkstyle 一樣生年,在安裝后之后重啟 Android studio婴程。值得注意的是 Findbugs 分析的是 Java 字節(jié)碼,所以在啟用 Findbugs 之前要保證你的工程是編譯過(guò)的抱婉,在 FIndbugs 掃描之后档叔,如果發(fā)現(xiàn)問(wèn)題,會(huì)在對(duì)應(yīng)的代碼出給出提示:
2.2 Gradle 腳本使用
在Gradle使用非常簡(jiǎn)單蒸绩,下面的腳本展示了如何 FindBugs:
task findbugs(type: FindBugs) {
ignoreFailures = false
effort = "max"
reportLevel = "high"
excludeFilter = new File("${project.rootDir}/config/quality/findbugs/findbugs-filter.xml")
classes = files("${project.rootDir}/app/build/classes")
source 'src'
include '**/*.java'
exclude '**/gen/**'
reports {
xml.enabled = false
html.enabled = true
xml {
destination "$project.buildDir/reports/findbugs/findbugs.xml"
}
html {
destination "$project.buildDir/reports/findbugs/findbugs.html"
}
}
classpath = files()
}
腳本任務(wù)和 Checkstyle 類似衙四,F(xiàn)indBugs 可以根據(jù)我們指定的范圍進(jìn)行掃描,這個(gè)范圍我們可以通過(guò)一個(gè)過(guò)濾規(guī)則文件來(lái)制定掃描結(jié)果報(bào)告支持 HTML 和 XML 兩種格式侵贵。excludeFilter 指定了過(guò)濾器配置文件届搁,reports 指定了檢測(cè)報(bào)告的文件格式和文件地址缘薛。執(zhí)行 Findbugs 的 task 非常簡(jiǎn)單窍育,和 Checkstyle 一樣。
2.3 Findbugs 使用技巧
我強(qiáng)烈建議為 Findbugs 配置一個(gè)過(guò)濾文件宴胧,因?yàn)?Android 工程和 Java 工程稍微有些不一樣漱抓,Android 工程自動(dòng)生成的 R 文件并不符合 Findbugs 的規(guī)范,需要過(guò)濾掉恕齐。另外要注意的是:Findbugs 分析的是字節(jié)碼乞娄,你需要先編譯,再進(jìn)行 Findbugs 的分析显歧。
3 PMD
這個(gè)工具比較有趣:其實(shí) PMD 真正的名字并不是 PMD 仪或。 在其官方網(wǎng)站上你會(huì)發(fā)現(xiàn)兩個(gè)非常有趣的名字:
- Pretty Much Done
- Project Meets Deadline
事實(shí)上 PMD 是一個(gè)非常強(qiáng)大的工具,它的作用類似 Findbugs士骤,但是它的檢測(cè)掃描是基于源碼的范删,而且 PMD 不僅僅能檢測(cè) Java 語(yǔ)言,還能檢測(cè)其他語(yǔ)言拷肌。PMD 的目標(biāo)和 Findbugsd 非常的相似到旦,都是通過(guò)定義的規(guī)則靜態(tài)分析代碼中可能出現(xiàn)的錯(cuò)誤,為什么要同時(shí)使用 PMD 和 Findbugs呢巨缘?由于 Findbugs 和 PMD 的掃描方式不一樣添忘,PMD 能發(fā)現(xiàn)的一些 Findbugs 發(fā)現(xiàn)不了的問(wèn)題,反之亦然若锁。
3.1 Android 插件中使用
插件的下載過(guò)程不再贅述搁骑,安裝完成重啟之后,到頂部菜單 Tools 欄目可以找到 QAplug 選項(xiàng),可以執(zhí)行代碼分析:
執(zhí)行完成靶病,會(huì)在控制臺(tái)輸出結(jié)果:
3.2 在 Gradle 腳本中使用
下面的腳本代碼展示了如何使用PMD:
task pmd(type: Pmd) {
ruleSetFiles = files("${project.rootDir}/config/quality/pmd/pmd-ruleset.xml")
ignoreFailures = false
ruleSets = []
source 'src'
include '**/*.java'
exclude '**/gen/**'
reports {
xml.enabled = false
html.enabled = true
xml {
destination "$project.buildDir/reports/pmd/pmd.xml"
}
html {
destination "$project.buildDir/reports/pmd/pmd.html"
}
}
}
配置都和 Findbus 如出一轍会通,PMD 同樣也可以輸出 HTML 和 XML 報(bào)告,例子中選中的是 HTML 格式娄周。我強(qiáng)烈建議你定義自己的 rulesets 文件(規(guī)則集合)涕侈,關(guān)于 rulesets的配置,可以參考[官方文檔](http://pmd.sourceforge.net/pmd- 5.1.1/howtomakearuleset.html)煤辨,PMD存在爭(zhēng)議的規(guī)則比 Findbugs 要多裳涛,例如對(duì)于嵌套的 “if statement” 它總是提醒你 “These nested if statements could be combined”,或者對(duì)空的 “if statement ” 總是提醒你 “Avoid empty if statements”众辨,不過(guò)端三,我覺(jué)得是否需要把嵌套 “if statement” 合并到一個(gè) “if statement” 取決于你或者你的團(tuán)隊(duì)自己來(lái)定義,我不太建議合并 “if statement ” 這樣會(huì)降低代碼可讀性鹃彻。執(zhí)行 PMD 的 task 非常簡(jiǎn)單郊闯,和 Checkstyle 一樣。
4 Android Lint
“The Android lint tool is a static code analysis tool that checks your Android project source files for potential bugs and optimization improvements for correctness, security, performance, usability, accessibility, and internationalization.” 正如官網(wǎng)所說(shuō)蛛株,Android Lint 是另一個(gè)靜態(tài)代碼分析工具,專門針對(duì) Android 工程团赁。Android Lint 除了對(duì)代碼掃描,分析潛在問(wèn)題之外谨履,還能對(duì)Android的資源進(jìn)行檢測(cè)欢摄,無(wú)用的資源,錯(cuò)位的dip資源等笋粟。
4.1 Gradle 腳本使用
android {
lintOptions {
abortOnError true
lintConfig file("${project.rootDir}/config/quality/lint/lint.xml")
// if true, generate an HTML report (with issue explanations, sourcecode, etc)
htmlReport true
// optional path to report (default will be lint-results.html in the builddir)
htmlOutput file("$project.buildDir/reports/lint/lint.html")
}
我建議你單獨(dú)指定一個(gè)配置文件來(lái)決定是否過(guò)濾一些規(guī)則怀挠,規(guī)則的定義可以參考最新ADT給出的規(guī)則,參考這里害捕。使用 “severity” 配置為 “ignore” 來(lái)過(guò)濾指定的規(guī)則绿淋。 執(zhí)行 Lint 和執(zhí)行 Checkstyle 的 task 一樣。執(zhí)行完成之后到結(jié)果輸出目錄中查看報(bào)告尝盼,例如下面是我(譯者)執(zhí)行自己的工程輸出的部分截圖:
5 在一個(gè)任務(wù)統(tǒng)一使用以上工具
以上介紹完了四個(gè)工具吞滞,現(xiàn)在我們來(lái)看看如何一次同時(shí)運(yùn)行四個(gè)工具?我們可以管理 gradle task 之間的依賴關(guān)系东涡,使得我們?cè)趫?zhí)行一個(gè) task 任務(wù)的同時(shí)其他 task 也能被執(zhí)行冯吓。使用 Gradle 提供的方法,我們可以把四個(gè)工具的執(zhí)行任務(wù)添加為 “check” task 的依賴:
check.dependsOn 'checkstyle', 'findbugs', 'pmd', 'lint'
現(xiàn)在疮跑,只要我們只想 “check” 這個(gè) task 组贺,Checkstyle、Windbags祖娘、PMD 和 Android Lint 都會(huì)自動(dòng)執(zhí)行失尖。在 commit/push/merge request 之前 執(zhí)行一下 check 任務(wù)啊奄,對(duì)我們代碼質(zhì)量的提高將是一種非常棒的方式。執(zhí)行這個(gè)任務(wù)比較簡(jiǎn)單掀潮,你可以在命令行中執(zhí)行:
gradlew check
6 總結(jié)
正如上文所說(shuō)菇夸,在 Gradle 中使用這些工具是非常簡(jiǎn)單的。這些工具不僅能在本地使用仪吧,還能部署到我們的自動(dòng)化編譯服務(wù)器上庄新,比如 Jenkins/Hudson,自動(dòng)處理掃描我們的代碼并輸出報(bào)告薯鼠。