提高程式碼質量-工具篇

yangxi_001發表於2017-09-18

在這片文章中,我將從工具使用的角度上講述如何提高 Android 程式碼質量,這些自動化工具包括 Checksytle、Findbugs、PMD 和 Android Lint. 團隊中程式碼意識不一致,水平參差不齊,程式碼風格迥異,定下的規範也是熟視無睹。這時候就需要藉助工具的力量,利用工具自動地幫助我們檢測程式碼,避免程式碼惡習,預防蟻穴壞堤。

0.1 Fork 這個例子工程

我強烈建議你fork這個例子工程,所有的使用事列都會在這個demo中呈現,同時你可以測試你自己寫的規則。

0.2 關於 Gradle 的 Task

理解 Gradle 的 Task 是理解這篇文章的基礎,我強烈建議你多看看關於Gradle Task的文章(例如這篇還有這篇),當然,本文也是滿滿的例子,所以你很容易理解,這也是我建議你 fork 我的程式碼倉庫的原因,把工程匯入你的 Android studio,然後你將看到熟悉的 Gradle Task 指令碼。如果你還是不太理解,也不用擔心,我將最大努力的寫好註釋。

0.3 關於 這個 demo 的層級結構

Gradle 指令碼可以分散在不同的檔案中,我在工程中寫了三個 gradle 檔案:

1 Checkstyle


Check style 是一個幫助開發者維持編碼規範標準的一個工具,它能自動地檢測 Java 程式碼,以減少人工檢測程式碼的成本。當你啟用 Checkstyle,它能解析你的程式碼並能告訴你程式碼中的錯誤或者不符合定義的規範的地方。

1.1 Android Studio 外掛

Checkstyle 提供了多種IDE的外掛支援,Android Studio 也不例外。
進入Android Studio設定頁面,在外掛欄輸入Checkstyle:


Checkstyle外掛

如果你還沒安裝 Checkstyle 的外掛,進入下載頁下載,然後重啟 Android Studio。啟動 Android studio 後進入 Checkstyle 的設定頁面:


Checkstyle設定

在這裡我們可以設定 Checkstyle 外掛掃描範圍,配置檔案等資訊,預設使用的配置檔案是官方提供的檔案:sun_checks.xml,我們也可以根據自己專案的需要定義自己的配置檔案,配置規則可以參考官方文件
設定完成之後點選 “Apply” 或者 “ok” 按鈕,回到程式碼中,我們便可以看到 Checkstyle 給我們的提示:


Checksylte高亮提示

1.2 Gradle 方式使用Checkstyle

如果我們需要把 Checkstyle 繼承到自動編譯伺服器中,例如:jenkins,我們需要利用 Gradle Task來執行 Checkstyle,下面這段指令碼展示了在 Gradle task 的 Checkstyle 的基本配置:

<code class="bash">task checkstyle(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">type</span>: Checkstyle) {
    configFile file(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">${project.rootDir}</span>/config/quality/checkstyle/checkstyle.xml"</span>) // Where my    checkstyle config is...
    configProperties.checkstyleSuppressionsPath = file(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">${project.rootDir}</span>/config/quality/checkstyle/suppressions.xml"</span>).absolutePath // Where is my suppressions file <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> checkstyle is...
    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span> <span class="hljs-string" style="color: rgb(0, 136, 0);">'src'</span>
    include <span class="hljs-string" style="color: rgb(0, 136, 0);">'**/*.java'</span>
    exclude <span class="hljs-string" style="color: rgb(0, 136, 0);">'**/gen/**'</span>
    classpath = files()
}</code>

這個 task 將會根據你指定的 checkstyle.xml 和 suppressions.xml 檔案來分析你的程式碼。你可以在 Android Studio 中執行這個任務:


執行完成之後,checkstyle 工具將會把每一個不合法的問題顯示在控制檯中。

2 FindBugs


FindBugs 這個名字本身已經揭示了它的作用,“FindBugs uses static analysis to inspect Java bytecode for occurrences of bug patterns.” FindBugs 是一個工具,它能通過靜態分析方式掃描 Java 位元組碼,發現其中的可能出現 bug 的程式碼,它能發現一些常規的低階的錯誤,例如一些錯誤的邏輯操作,也能發現一些比較隱晦的錯誤。
例如:

<code class="cs">   Person person = (Person) map.<span class="hljs-keyword" style="color: rgb(0, 0, 136);">get</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">"bob"</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (person != <span class="hljs-keyword" style="color: rgb(0, 0, 136);">null</span>) {
        person.updateAccessTime();
    }
    String name = person.getName();</code>

最後一行程式碼,可能會出現空指標錯誤。
又如:

<code class="objectivec">    b<span class="hljs-variable" style="color: rgb(102, 0, 102);">.replace</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">'b'</span>, <span class="hljs-string" style="color: rgb(0, 136, 0);">'p'</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span>(b<span class="hljs-variable" style="color: rgb(102, 0, 102);">.equals</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">"pop"</span>)) {
        Log<span class="hljs-variable" style="color: rgb(102, 0, 102);">.d</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">""</span>,<span class="hljs-string" style="color: rgb(0, 136, 0);">""</span>);
    }</code>

b.replace('b', ‘p’);這段程式碼對b不會產生影響,所以是無效的。

2.1 Android Studio 外掛

同樣,FIndbugs 也提供了 Android Studio 的外掛支援,外掛的獲取過程和 Checkstyle 一樣,在安裝後之後重啟 Android studio。值得注意的是 Findbugs 分析的是 Java 位元組碼,所以在啟用 Findbugs 之前要保證你的工程是編譯過的,在 FIndbugs 掃描之後,如果發現問題,會在對應的程式碼出給出提示:


findbugs提示

2.2 Gradle 指令碼使用

在Gradle使用非常簡單,下面的指令碼展示瞭如何 FindBugs:

<code class="bash">task findbugs(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">type</span>: FindBugs) {
    ignoreFailures = <span class="hljs-literal" style="color: rgb(0, 102, 102);">false</span>
    effort = <span class="hljs-string" style="color: rgb(0, 136, 0);">"max"</span>
    reportLevel = <span class="hljs-string" style="color: rgb(0, 136, 0);">"high"</span>
    excludeFilter = new File(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">${project.rootDir}</span>/config/quality/findbugs/findbugs-filter.xml"</span>)
    classes = files(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">${project.rootDir}</span>/app/build/classes"</span>)
    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span> <span class="hljs-string" style="color: rgb(0, 136, 0);">'src'</span>
    include <span class="hljs-string" style="color: rgb(0, 136, 0);">'**/*.java'</span>
   exclude <span class="hljs-string" style="color: rgb(0, 136, 0);">'**/gen/**'</span>

    reports {
       xml.enabled = <span class="hljs-literal" style="color: rgb(0, 102, 102);">false</span>
        html.enabled = <span class="hljs-literal" style="color: rgb(0, 102, 102);">true</span>
        xml {
       destination <span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">$project</span>.buildDir/reports/findbugs/findbugs.xml"</span>
   }
    html {
        destination <span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">$project</span>.buildDir/reports/findbugs/findbugs.html"</span>
    }
}

    classpath = files()
}</code>

指令碼任務和 Checkstyle 類似,FindBugs 可以根據我們指定的範圍進行掃描,這個範圍我們可以通過一個過濾規則檔案來制定掃描結果報告支援 HTML 和 XML 兩種格式。excludeFilter 指定了過濾器配置檔案,reports 指定了檢測報告的檔案格式和檔案地址。執行 Findbugs 的 task 非常簡單,和 Checkstyle 一樣。

2.3 Findbugs 使用技巧

我強烈建議為 Findbugs 配置一個過濾檔案,因為 Android 工程和 Java 工程稍微有些不一樣,Android 工程自動生成的 R 檔案並不符合 Findbugs 的規範,需要過濾掉。另外要注意的是:Findbugs 分析的是位元組碼,你需要先編譯,再進行 Findbugs 的分析。

3 PMD


這個工具比較有趣:其實 PMD 真正的名字並不是 PMD 。 在其官方網站上你會發現兩個非常有趣的名字:

  • Pretty Much Done
  • Project Meets Deadline

事實上 PMD 是一個非常強大的工具,它的作用類似 Findbugs,但是它的檢測掃描是基於原始碼的,而且 PMD 不僅僅能檢測 Java 語言,還能檢測其他語言。PMD 的目標和 Findbugsd 非常的相似,都是通過定義的規則靜態分析程式碼中可能出現的錯誤,為什麼要同時使用 PMD 和 Findbugs呢?由於 Findbugs 和 PMD 的掃描方式不一樣,PMD 能發現的一些 Findbugs 發現不了的問題,反之亦然。

3.1 Android 外掛中使用

外掛的下載過程不再贅述,安裝完成重啟之後,到頂部選單 Tools 欄目可以找到 QAplug 選項,可以執行程式碼分析:


PMD程式碼分析

執行完成,會在控制檯輸出結果:


PMD執行結果

3.2 在 Gradle 指令碼中使用

下面的指令碼程式碼展示瞭如何使用PMD:

<code class="bash">task pmd(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">type</span>: Pmd) {
    ruleSetFiles = files(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">${project.rootDir}</span>/config/quality/pmd/pmd-ruleset.xml"</span>)
    ignoreFailures = <span class="hljs-literal" style="color: rgb(0, 102, 102);">false</span>
    ruleSets = []

   <span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span> <span class="hljs-string" style="color: rgb(0, 136, 0);">'src'</span>
   include <span class="hljs-string" style="color: rgb(0, 136, 0);">'**/*.java'</span>
   exclude <span class="hljs-string" style="color: rgb(0, 136, 0);">'**/gen/**'</span>

    reports {
        xml.enabled = <span class="hljs-literal" style="color: rgb(0, 102, 102);">false</span>
        html.enabled = <span class="hljs-literal" style="color: rgb(0, 102, 102);">true</span>
    xml {
        destination <span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">$project</span>.buildDir/reports/pmd/pmd.xml"</span>
    }
   html {
        destination <span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">$project</span>.buildDir/reports/pmd/pmd.html"</span>
       }
    }
}</code>

配置都和 Findbus 如出一轍,PMD 同樣也可以輸出 HTML 和 XML 報告,例子中選中的是 HTML 格式。我強烈建議你定義自己的 rulesets 檔案(規則集合),關於 rulesets的配置,可以參考官方文件,PMD存在爭議的規則比 Findbugs 要多,例如對於巢狀的 “if statement” 它總是提醒你 “These nested if statements could be combined”,或者對空的 “if statement ” 總是提醒你 “Avoid empty if statements”,不過,我覺得是否需要把巢狀 “if statement” 合併到一個 “if statement” 取決於你或者你的團隊自己來定義,我不太建議合併 “if statement ” 這樣會降低程式碼可讀性。執行 PMD 的 task 非常簡單,和 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.” 正如官網所說,Android Lint 是另一個靜態程式碼分析工具,專門針對 Android 工程。Android Lint 除了對程式碼掃描,分析潛在問題之外,還能對Android的資源進行檢測,無用的資源,錯位的dip資源等。

4.1 Gradle 指令碼使用

<code class="bash">android {
    lintOptions {
    abortOnError <span class="hljs-literal" style="color: rgb(0, 102, 102);">true</span>
     lintConfig file(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">${project.rootDir}</span>/config/quality/lint/lint.xml"</span>)
    // <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-literal" style="color: rgb(0, 102, 102);">true</span>, generate an HTML report (with issue explanations, sourcecode, etc)
    htmlReport <span class="hljs-literal" style="color: rgb(0, 102, 102);">true</span>
    // optional path to report (default will be lint-results.html <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span> the builddir)
    htmlOutput file(<span class="hljs-string" style="color: rgb(0, 136, 0);">"<span class="hljs-variable" style="color: rgb(102, 0, 102);">$project</span>.buildDir/reports/lint/lint.html"</span>)
}</code>

我建議你單獨指定一個配置檔案來決定是否過濾一些規則,規則的定義可以參考最新ADT給出的規則,參考這裡。使用 “severity” 配置為 “ignore” 來過濾指定的規則。 執行 Lint 和執行 Checkstyle 的 task 一樣。執行完成之後到結果輸出目錄中檢視報告,例如下面是我(譯者)執行自己的工程輸出的部分截圖:


5 在一個任務統一使用以上工具

以上介紹完了四個工具,現在我們來看看如何一次同時執行四個工具?我們可以管理 gradle task 之間的依賴關係,使得我們在執行一個 task 任務的同時其他 task 也能被執行。使用 Gradle 提供的方法,我們可以把四個工具的執行任務新增為 “check” task 的依賴:

<code class="sql"><span class="hljs-operator"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">check</span>.dependsOn <span class="hljs-string" style="color: rgb(0, 136, 0);">'checkstyle'</span>, <span class="hljs-string" style="color: rgb(0, 136, 0);">'findbugs'</span>, <span class="hljs-string" style="color: rgb(0, 136, 0);">'pmd'</span>, <span class="hljs-string" style="color: rgb(0, 136, 0);">'lint'</span></span></code>

現在,只要我們只想 “check” 這個 task ,Checkstyle、Windbags、PMD 和 Android Lint 都會自動執行。在 commit/push/merge request 之前 執行一下 check 任務,對我們程式碼質量的提高將是一種非常棒的方式。執行這個任務比較簡單,你可以在命令列中執行:

<code class="sql">    gradlew <span class="hljs-operator"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">check</span></span></code>

6 總結

正如上文所說,在 Gradle 中使用這些工具是非常簡單的。這些工具不僅能在本地使用,還能部署到我們的自動化編譯伺服器上,比如 Jenkins/Hudson,自動處理掃描我們的程式碼並輸出報告。

轉自:http://blog.csdn.net/dj0379/article/details/51959134

相關文章