官术网_书友最值得收藏!

2.3 使用CheckStyle進行代碼審查

和Android Lint相比,CheckStyle同樣是一種靜態代碼檢查工具的。內置的檢查規則種類超過百種,主要側重點在代碼風格、編程規范等,支持強大、靈活的自定義規則檢查。

2.3.1 運行CheckStyle

本小節中,我們介紹運行CheckStyle的兩種方式:在Android Studio中使用插件以及作為獨立的命令行工具運行。

下面我們逐個進行講解。

1.在Android Studio中運行檢查

在2.1.2小節中,我們已經成功地安裝了CheckStyle插件。在Android Studio工作區的左下角可以找到CheckStyle視圖,一般在這個視圖中運行檢查,如圖2.23所示。

圖2.23 CheckStyle視圖

初始環境中,插件包含Google和Sun的代碼檢查規則,可以通過Rules(規則)下拉菜單選擇某個規則,然后單擊左側的開始按鈕啟動檢查。圖2.24是檢查結果的示例。

圖2.24 使用Google代碼規則檢查結果

通過插件的方式進行檢查,方法簡單,但需要啟動Android Studio才能運行,而且想要執行檢查,還要保證工程Build結束才可以。若要拋開這些限制,并使用更豐富的選項執行檢查,則不得不使用命令行模式。

下面我們就來介紹如何通過命令行啟動檢查。

2.在命令行啟動檢查

(1)下載CheckStyle可執行JAR文件

若要使用命令行模式啟動檢查,則首先要具備CheckStyle的可執行JAR文件。這個文件可以到CheckStyle的GitHub開源庫下載,也可以到Maven倉庫中下載。如圖2.25所示,我們選擇在GitHub下載。

圖2.25 下載CheckStyle

截至目前,最新的版本號是8.28。單擊checkstyle-8.28-all.jar開始下載。

(2)運行CheckStyle

作為獨立運行的CheckStyle提供了很多選項,這些選項的說明可以在官網找到詳細解釋,這里選擇幾個常用的選項進行講解。

先來看一個較為簡單的用法,假設我們依舊使用Google的代碼規則,并已經取得了相應的XML規則描述文檔,其名稱為google_checks.xml。該文檔和checkstyle-8.28-all.jar位于My Application工程的checkstyle目錄中,如圖2.26所示。

圖2.26 CheckStyle目錄結構

接下來,打開macOS的終端(在Windows操作系統中為命令提示符),定位到checkstyle目錄下,執行下面的命令:

得到如圖2.27所示的輸出。

圖2.27 CheckStyle命令行模式檢查結果

顯而易見,總共有6個警告級別的問題,和在Android Studio中的結果一致。

(3)CheckStyle命令詳解

下面來拆解前面所執行的命令。

如圖2.28所示,整條命令可分解成三部分。第一部分是運行.jar文件的通用方法,相信有Java編程基礎的讀者并不陌生;第二部分是CheckStyle工具的參數,-c即configurationFile,表示使用哪種代碼規范檢查(特別注意的是,該參數為唯一的必選參數。當未指定配置文件時運行,將會收到報錯提示);最后一部分是要檢查的代碼路徑,“../”表示向上一層,此處的含義是回到工程根目錄,在實際使用中,這部分可以是一個目錄,也可以是特定的某個文件。

圖2.28 CheckStyle命令拆解圖示

如果用戶有其他的配置文件,那么不妨嘗試使用此種方式運行檢查。無須啟動Android Studio,更無須完成Build,只需要執行上述CheckStyle命令即可。

(4)CheckStyle常用命令

看到這里,你可能會有這些疑問:如何將檢查的結果保留下來呢?如果想排除某個目錄/文件,該怎么做呢?命令行不如UI界面直觀,如果忘記了用法,該怎樣獲得幫助呢?

帶著這3個疑問,我們來了解CheckStyle常用的3個參數。

首先解答第一個問題:如何保留檢查結果。

CheckStyle使用-o參數來指定文件的路徑,這個文件即結果的輸出文件。它的使用方法如下:

執行該命令后,控制臺沒有任何輸出。在任務完成后,我們可以在同級目錄下找到result.txt文件。使用任意文本編輯器打開它,其內容如圖2.29所示。

圖2.29 CheckStyle檢查結果

此外,我們還可以使用-f參數來指定結果輸出的格式。CheckStyle提供了兩種格式:一種是上面默認的純文本格式;另一種是XML格式,必要時可以使用后者。

接下來回答第二個問題:如何排除某個文件或目錄。添加例外的情況通常用于多Module的場景中,觀察如圖2.30所示的工程結構。

圖2.30 多于一個Module的工程結構

圖2.30中共有兩個Module,即app和submodule,且各自都有Java源碼,現在要做的是排除所有無須做檢查的源碼。這里所說的無須做檢查,包含除MainActivity.java、SubMainActivity.java以及XML以外的所有文件。

當然,針對本例,可以指定僅檢查這兩個文件而非使用排除法,且這樣做更加方便。此處使用排除法僅為說明如何正確排除文件。通常在實際開發中,使用排除法更為常見。

要找到所有無關的文件,先要清楚整個工程的文件組織結構。

如圖2.31所示,需要排除的文件是除了用方框框起來的兩個Java源代碼文件以及若干XML源代碼文件外的所有文件。這看上去似乎很煩瑣,但好消息是:由于我們要使用的Google檢查規則只對Java、Properties和XML源代碼文件有效,因此只需要排除兩個Module中的test相關類即可。

圖2.31 多于一個Module的目錄結構

CheckStyle使用-e或-x參數添加例外,前者要求給定一個或多個具體清晰的路徑,后者要求給定一個或多個描述路徑的正則表達式,當有文件或目錄匹配到給定的正則表達式時,相應的文件或目錄將被跳過。

思路比較簡單,但是寫起來比較麻煩,就是使用多個-e參數將兩個Module中的androidTest和test目錄排除在外,即:

如果你對正則表達式比較熟悉,還可以使用-x參數,配合正則表達式讓計算機程序幫助找到符合條件的項目,并將其排除在外。

本例中,只要排除名為androidTest和test的目錄即可,因此,可以使用下面的命令:

此外,-e和-x參數還可以同時使用。如果只想檢查submodule模塊的有用代碼,可以執行下面的命令:

最后,如果忘記了命令行的使用方法,該怎樣快速獲得幫助呢?

這里給大家提供兩種方法,分別說明如下:

第一種方法是使用--help,即java -jar checkstyle-8.29-all.jar –help,如圖2.32所示。

圖2.32 CheckStyle本地幫助文檔

第二種方法是到CheckStyle官網查詢相應的文檔獲得幫助,如圖2.33所示。

圖2.33 CheckStyle在線幫助文檔

這兩種方法相比,第一種更快速,第二種有使用示例,更詳細。讀者日后可根據實際需要進行選擇。

2.3.2 自定義CheckStyle檢查規則

接下來,我們來介紹如何自定義CheckStyle配置文件,從而打造屬于自己或團隊的代碼檢查規范。

1.Module(模塊)

我們都知道,CheckStyle的配置文件是XML格式的文檔。通過閱讀Sun和Google的代碼檢查規范可以發現:整個XML文檔呈樹形結構,其根是一個名為Checker的Module,該Module又包含若干子Module。這些子Module包含4種分類:

(1)FileSetChecks:定義了具體的檢查規則,用于檢查相應的源代碼文件。

(2)Filters:用于過濾檢查規則。

(3)File Filters:用于過濾要檢查的文件。

(4)AuditListeners:報告已接收的事件。

細心的讀者會發現,大部分檢查規則都被一個叫作TreeWalker的Module包括。根據上文所述的分類,這個名為TreeWalker的Module屬于FileSetCheck Module。它的原理就是將每個待檢查的Java源代碼文件轉換為抽象語法樹(與具體語法樹/分析樹相對,抽象語法樹可簡稱為語法樹(Abstract Syntax Tree, AST)。它是源代碼語法結構的一種抽象表示。它以樹狀的形式表現編程語言的語法結構,樹上的每個節點都表示源代碼中的一種結構。之所以說語法是“抽象”的,是因為這里的語法并不會表示出真實語法中出現的每個細節。更多詳情讀者可參閱編譯原理中的相關知識點),然后由其中的子Module按照各自定義的規則進行逐項檢查,直到全部完成。

2.Properties(屬性)

Checker定義了一些通用的屬性,這些屬性被其他的Module繼承和使用。這些屬性的定義如下:

· basedir:基本目錄名,在有關文件的描述信息中將被去掉,默認值為空。

· cacheFile:緩存所有檢查完畢的文件,通常用于避免重復檢查,默認值為空。

· localeContry:描述信息所使用的區域編碼,其默認值取決于Java虛擬機。

· localeLanguage:描述信息所使用的語言編碼,其默認值取決于Java虛擬機。

· charset:字符集名稱,默認值取決于名為file.encoding的系統屬性值。

· fileExtensions:接受檢查的文件擴展名,默認檢查所有文件。

· sverity:定義所有違反代碼編寫規則的嚴重級別,默認為error。

· haltOnException:在檢查過程中,如果發生異常,是否停止檢查。默認為true。

· tabWidth:定義了一個制表符包含的空格數量,其默認值為8。

現在,打開Google和Sun的代碼規則配置文件,找到相關屬性,看看它們是如何定義的。

3.Metadata(元數據)

CheckStyle允許使用Metadata來保存特定的信息,這些信息可以用來保存插件特定信息或其他的有用信息,這些信息在執行代碼檢查時會被忽略。需要注意的是,為了避免和其他工具或插件發生命名沖突,建議將所有Metadata名稱加上特定的前綴。

參考下面的例子:

4.TreeWalker規則定義

如前文所述,TreeWalker及其子Module定義了詳細明確的代碼檢查規則。如果想要對某一個規則項進行檢查,那么只需要添加相應的子Module即可。而且子Module的規則是可以被定制的。

舉個例子,對于工程My Application,以Google的代碼檢查規范運行檢查,其結果總共有6個warning級別的問題輸出。現在,我們把目光聚集在CustomImportOrder規則,由該規則檢查出的問題總共有兩個,如圖2.34所示。

圖2.34 違反CustomImportOrder規則的代碼位置

打開google_checks.xml,搜索CustomImportOrder,找到如下代碼片段:

嘗試注釋掉CustomImportOrder部分代碼,然后再次運行檢查,發現問題數量變為4,不再有違反CustomImportOrder規則的報告。

這很好理解,對于某個規則,如果要添加相應的檢查,就需要聲明它,聲明的方式只需添加相應Module即可;未被添加的Module將默認不會被檢查。那么,在哪里可以找到所有的檢查類別呢?我們在CheckStyle官網中找到了答案。

如圖2.35所示,CheckStyle網站上羅列了所有的檢查規則,每項規則還有詳盡的描述。讀者在使用時可根據實際需要,按關鍵字進行模糊查找,再通過描述來確定是否使用某項規則。

圖2.35 CheckStyle規則文檔

此外,在CheckStyle的GitHub開源庫中,checkstyle_check.xml文件中包含所有的檢查規則,我們也可以將其作為參考。

現在,讓我們繼續針對上文中的CustomImportOrder規則做實驗。很明顯,在規則配置文檔的CustomImportOrderModule中還有若干Property。我們嘗試去掉所有的Property,然后再次運行檢查,發現之前的6個問題變成了5個,減少了一個違反CustomImportOrder規則的項目。這是為什么呢?

實際上,對于每個Module,當我們添加它時,實際上也同時添加了它的默認屬性。

就拿CustomImportOrder來說,它的默認規則如圖2.36所示。

圖2.36 CustomImportOrder規則屬性及默認值

由于這些屬性有類似Java中的Override特性,因此通過對比google_checks.xml中的自定義項,我們發現名為sortImportsInGroupAlphabetically屬性的值是true,而非默認的false。正是這個原因,讓我們從一開始就檢查出了6個問題項。

好了,總結一下,對于TreeWalker規則定義:

(1)要增加某種規則檢查,需要聲明對應的Module,聲明的方式即添加子Module項。

(2)利用Property可覆蓋默認值的特性,我們可以自定義屬于自己或團隊的統一編碼規則檢查配置文件。

(3)所有的檢查規則可以在CheckStyle網站或GitHub開源庫的checkstyle_check.xml中找到詳細描述。

5.Severity(嚴重性)

我們已經使用Google的代碼規則對My Application項目做過一次代碼檢查了,其結果共有6個warning項。在CheckStyle中,還可以對某類檢查結果自定義嚴重性級別。

打開google_checks.xml文檔,在22行左右發現了如下代碼:

CheckStyle使用severity屬性定義和它相關Module的嚴重級別,在該Module中的所有子Module若未單獨聲明該屬性,則默認繼承父Module的嚴重級別定義。默認情況下,嚴重級別為error。

在google_checks.xml中,其對應的Module為Checker,因此所有子Module報告的問題嚴重級別皆為warning。若去掉嚴重性定義,則原來的6個warning項將變為error,命令行也會有匯總提示,如圖2.37所示。

圖2.37 默認嚴重級別下的結果輸出

6.ID(唯一標識符)

打開google_checks.xml,搜索SeparatorWrap。我們驚訝地發現,居然有4個相同的Module,唯一不同的是其中的Property。這是怎么回事呢?

出現同種Module其實在CheckStyle中是允許的。這種情況適用于不同的情境下進行檢查。而決定要用哪一個Module,是由其中名為id的Property決定的。因此,Module可以同名,但id必須唯一。這樣做,一方面保證了代碼檢查的靈活性,另一方面解決了多個相同Module之間的沖突。

在google_checks.xml文檔中,總共有5個SeparatorWrap Module,其id均不同。在不同的環境中,將啟用各自id的Module。

主站蜘蛛池模板: 克东县| 林西县| 榆中县| 太白县| 清水河县| 萍乡市| 彝良县| 长汀县| 江永县| 丰台区| 洮南市| 罗平县| 丽江市| 长寿区| 奉节县| 台北县| 侯马市| 阿克苏市| 旅游| 青龙| 长子县| 赣榆县| 九台市| 湘西| 贡嘎县| 商城县| 清远市| 上犹县| 灵寿县| 英山县| 英吉沙县| 大洼县| 青州市| 巨鹿县| 江门市| 内乡县| 长泰县| 阿拉尔市| 武威市| 鄂托克旗| 连州市|