天天看點

聊聊我所知道的 Android 相關的代碼檢測

因為筆者業務開發能力不太夠,是以會經常做一些周邊的雜活。曾經做過在團隊裡建立代碼檢測機制的工作,是以我想簡單地說下這部分的内容。

首先要說的是 軟體開發的檢測 的目的是什麼,以及都有那些類型的檢測。

在我看來,檢測的目的就是為了 保證代碼可以滿足某些要求 。

以筆者淺顯的見識,現在覺得一般的軟體開發檢測包括:

代碼檢測

業務功能的單元測試

子產品整體(比如一個 aar / 一個 apk)的自動化測試

真人 QA 介入的測試

其中從上到下,

測試執行成本(設計成本、人工成本等)越來越大;

測試執行所用時間越來越長;

測試執行頻率越來越低;

發現問題以後修複的成本越來越高;

今天隻讨論其中 代碼檢測 這個環節,其餘的環節因為筆者了解的并不多,就不在本次獻醜了。

筆者認為 代碼檢測 指的是開發者從 提出代碼修改 到 初步确認代碼沒有簡單的錯誤 的這個過程的。因為将 業務功能 相關的單元測試劃分出去了,是以這部分 代碼檢測 是不包含業務功能的檢測的。也就是說,這部分隻進行

靜态代碼的格式檢測

靜态代碼的健壯性的檢測

這樣子說可能比較模糊,我舉幾個現在比較常見的檢測規則:

改動是否包含魔數

改動裡的變量名是否符合項目的要求(比如要求靜态變量使用大寫命名,屬性變量使用駝峰格式命名)

改動是否導緻檔案過于龐大(比如說單個檔案超過了 1200 行,很難讓人讀懂)

改動是否導緻類繼承層級過多(比如改完了以後目前類到 Activity 有五層繼承關系)

改動是否導緻某個方法過于複雜(比如單個方法行數超過了 150 行,某個方法裡的 if / for / when 過多等)

……

這些檢測,在一些手熟的開發者手中,是很難出現的,因為這些人大都會很注意程式的基本設計,會有意識避免這些會導緻代碼可讀性變差的寫法。不過對于一些項目比較着急、或者像筆者這樣不太注意的開發者來說,就很容易寫出這種代碼。

這種代碼其實在 Code Review 環節是很容易發現的,不過如果單純依靠人來做這樣的檢測的話,一是比較慢,二是比較累。是以最好的方法就是增加自動檢測的機制,如果不符合這些條件的話,可以直接提示出來進行改動。這樣,通過檢測的代碼就有了一個最基本的品質底線。這也就是上面提到的 代碼檢測 了。從這個角度說,代碼檢測 其實是用來 1)規範代碼基本寫法 2)幫助 Code Review 的。

對于開發者來說,開發上面這種檢測插件無疑是很簡單的事情。但是對于筆者來說,開發這些東西需要檢視大量文法解析的資料,是以還是采用已經有的開源方案了。

對于 Android 上要進行的代碼檢測,有這幾種可選的方案:

純靜态檢測文本的方式解析代碼

解析代碼關系,檢測代碼

接下來分别介紹

這種檢測方式,類似于 python 這類“解析型語言”。特點是隻需要輸入一個 java 檔案,不需要它所依賴的類的檔案(比如說父類的類檔案),即可以完成檢測。甚至不需要整個檔案,隻輸入其中的一個方法也可以進行檢測!因為其隻依賴文本内容的特征,是以筆者稱之為靜态檢測代碼。

這部分已有比較成熟的開源方案。其中對于 java 有大名鼎鼎的 <code>check-style</code>; 對于 kotlin 則有新秀檢測方案 <code>detekt</code> 。兩者的接入方式比較一樣,使用方式也很類似。

限于篇幅,具體的用法就不在此轉發了,以官網的為準,各位朋友可以檢視官網的使用說明。

雖然要對付的語言不一樣,但是 <code>check-style</code> 和 <code>detekt</code> 如此相似,讓人不由想看看其内部實作的方式。簡單地檢視了各自的源碼後,會發現其實内部的原理都一樣,隻是表現的形式不同罷了。

兩者的思路其實大概流程都是下面這種流程(就不細說了,因為太詳細的話,筆者也不是很明白了;而且這裡的描述可能不夠清楚,如果看不明白的話,可能是因為筆者的了解不對或者是表達能力有限,可以先不關注。) 基于輸入的檔案内容解析出來一個文法樹。 會有多個寫好的檢測規則類,這些檢測規則其實就是 java文法樹周遊器 / kotlin 文法樹周遊器。 距離:比如說檢查檔案 在生成的文法樹上,周遊每一個元素: 對每一個元素,都周遊每一個規則。 上述的周遊完成後,即相當于每一個規則都周遊了該文法樹,也即相當于對輸入的檔案進行了一遍周遊。各個規則也都完成了對檔案的檢測,采集到了檔案不通過檢測的部分。 最後将所有不通過檢測的部分全都輸出,也就完成了文本的檢測了。

相比較于上面的類似 “解釋型語言” ,這種檢測方式更像 “編譯型語言” 。這種檢測方式隻依靠單一的輸入檔案是無法完成的,需要輸入的是一個可以作為整體進行“連結”的項目。比如 Android Studio 自帶的 lint 就是這類檢測方式。

舉個例子說明:

假設需要禁止一個類的子類太過龐大(比如繼承深度不能超過 5,每一代的繼承自雷數量不能超過 6)。

這類涉及多個檔案的檢測,明顯不是純文字可以完成的,需要将其他的檔案一并讀取分析才可以完成判斷。

這種檢測因為更加複雜,運作一次的用時也會更多,差不多類似于進行一次項目的編譯了。

但是因為筆者沒有實際運用過 lint 進行複雜的代碼檢測,是以在這裡不多談了。

代碼檢測是用來規範代碼格式的,對于保證代碼的最低品質還是比較有用的。當然了如果為了寫出更好的代碼,還是需要多多思考,少複制粘貼的。