天天看點

靜态測試技術之 Lint 備援資源清理

談到備援資源清理,我們不妨先來看看Android的資源組織方式和通路方式。

建立一個android工程後,預設資源路徑res下生成對應的layout、drawable、values等子目錄,分别對應以下幾類常見的非代碼資源:

靜态測試技術之 Lint 備援資源清理

layout,menu,anim等,代表res資源的頂層使用者,通過xml的方式組合控件,漸變動畫等資源,給Activity等元件提供視圖,通過這些xml腳本取代代碼實作的布局&動效,解耦視圖和界面邏輯,提升開發效率;

values,color,xml等,這一類代表res中的文本資源,都是xml格式資源,values主要存放arrays,attrs,colors,dimens,ids,string,integers,theme等基礎資源,支援比較豐富的語言擴充;color主要存放傳回color資源的selector資源;沒有明确歸屬目錄的xml資源,都可以放到xml目錄下;

drawable,drawabld-xxx,raw等,這一類代表res中的多媒體資源,有比較豐富分辨率擴充,其中drawable目錄主要存放傳回drawable格式的selector,帶nodpi标簽主要存放一些與分辨率無關的<code>9.png</code>資源,其他帶xxdpi等目錄對應相應的分辨率機型,沒有明确歸屬的非xml資源都可以放到raw目錄下。

在資源目錄中分類組織資源後,我們就可以通過引用資源 ID 來引用資源,所有資源的ID 都在項目中aapt工具自動生成的<code>./gen/R</code>類中定義,該檔案不能被手動修改,當資源發生變動時,它會相應更新。

通路資源的方法主要有兩種:

在代碼中:<code>R.resource_type.resource_name</code> (引用自定義資源) 或者<code>android.R.resource_type.resource_name</code>(引用系統标準資源),比如<code>R.string.hello</code>,string 是資源類型,hello 是資源名稱,API可以通過這種文法來通路定義的資源,如:<code>getResources().getString(R.string.hello)</code>;

在 XML 中:使用對應的XML 文法,<code>@[package:]type/name</code>,例如<code>@string/hello</code>,string 是資源類型,hello 是資源名稱,可以在 XML 資源通過該文法來通路定義的資源,如:<code>android:text="@string/hello"</code> 。

随着長時間的版本疊代,工程中會備援許多資源檔案,手動查找删除效率太低難免有漏網之魚,代碼掃描工具可以友善的查找出未被引用的圖檔、ID等資源,本文主要應用Android lint的unUsedResources規則進行備援資源查找清理。

Android Lint是針對Android的靜态代碼分析工具,能夠對Android項目中潛在的bug、可優化的代碼、安全性、性能、可用性、可通路性、國際化等進行檢查。

在Android SDKTools 16及更高的版本中,Lint工具會自動安裝。通過對Android工程源代碼等進行掃描檢查,可發現潛在的問題,更好的提升代碼品質。

通過lint進行備援資源清理主要有以下幾種方式:

1、我們可以通過<code>lint –check unUsedResources</code>查找備援資源清單然後手工或者通過其他删除工具加以清理。

2、如果工程使用的是gradle打包,可以在build.gradle中打開shrinkResources開關,這樣打包的時候不會把備援資源打包進來:<code>shrinkResources true //</code>移除無用的resource檔案

3、也可以在Android Studio中使用<code>Analyze-unUsedResources</code>項查找出所有未被引用的資源清單:

靜态測試技術之 Lint 備援資源清理

在結果上右鍵選擇<code>ApplyFix’Android Lint Quick Fixes’</code>,可以直接删除所有無用的資源:

靜态測試技術之 Lint 備援資源清理

可能存在的誤删除與白名單配置

lint掃描工具無法判斷出通過反射方式<code>(android.content.res.Resources#getIdentifier)</code>來擷取的資源,可能會産生誤删除,如:

靜态測試技術之 Lint 備援資源清理

此時資源被清理後界面上會找不到圖檔,如果工程中有該用法可以通過以下幾種方式對資源添加白名單配置:

1、局部配置:在XML檔案中通過<code>tools:ignore="UnusedResources"</code>屬性配置忽略:

在xml檔案開頭聲明命名空間tools,并為對應的element加上<code>tools:ignore="UnusedResources "</code>,<code>tools:ignore</code>屬性對其xml節點的所有子節點都生效:

2、 全局配置:在Android工程的根目錄下建立一個名叫<code>lint.xml</code>的檔案,如非xml資源可以通過這種方式添加白名單,IDE會讀取根目錄下的配置,指令行下可以通過—config指定具體配置,需要注意的是,如果工程根目錄下存在<code>lint.xml</code>時,<code>--config</code>指令指定的參數無效:

靜态測試技術之 Lint 備援資源清理

配置檔案中支援幾個次元的自定義配置:

(1)規則id級别調整,置為ignore則該規則不生效,如:

靜态測試技術之 Lint 備援資源清理

(2)路徑忽略,如:

靜态測試技術之 Lint 備援資源清理

(3)正規表達式忽略,如:

靜态測試技術之 Lint 備援資源清理

Lint掃描工具是如何掃描出備援資源的呢,我們先來認識下LintUnusedResources掃描規則,從源碼中規則的定義可以看到,UnusedResourceDetector繼承自ResourceXmlDetector和<code>Detector.JavaScanner</code> ,查找範圍包括Manifest,資源檔案,java源檔案及測試代碼:

靜态測試技術之 Lint 備援資源清理

1、根據R.java擷取資源清單:

Detetor類中JavaScanner接口定義的<code>getApplicableNodeTypes()</code>需要與<code>createJavaVisitor()</code>配合使用,<code>getApplicableNodeTypes()</code>傳回我們感興趣的Node清單,然後在<code>createJavaVisitor()</code>傳回的AstVisitor中去處理這些Node。

定義一個AstVisitor的子類,并在<code>createJavaVisitor()</code>中傳回它的一個執行個體,那麼當掃描到符合定義語句對應的node就會觸發<code>UnusedResourceVisitor()</code>中對應的回調函數:

靜态測試技術之 Lint 備援資源清理
靜态測試技術之 Lint 備援資源清理

2、查找代碼中的引用:

Detetor類中JavaScanner接口定義的<code>appliesToResourceRefsh()</code>需要與<code>visitResourceReference()</code>函數配合使用,<code>appliesToResourceRefsh()</code>傳回true,那麼代碼中的資源引用會觸發<code>visitResourceReference()</code>處理函數:

靜态測試技術之 Lint 備援資源清理

3、同樣的,查找xml檔案中的引用:

靜态測試技術之 Lint 備援資源清理

4、從收集到的資源聲明清單中删除被引用的資源清單并去除xml中聲明不做處理(如<code>tools:ignore="UnusedResources"</code>)或配置了白名單的資源,剩餘的資源清單可認為是備援資源:

靜态測試技術之 Lint 備援資源清理

5、report最終未被引用的資源清單:

靜态測試技術之 Lint 備援資源清理

清楚了lint備援資源的清理規則,我們可以放(小)心(心)地開始删删删了,謹慎起見,提供本地工具由開發童鞋本地清理确認,同時在持續內建平台自動監控備援資源清理情況,形成一鍵清理+自動監控的靈活處理模式:

1、一鍵清理:在lint掃描結果的基礎上提供指令行清理/還原工具,支援本地一鍵清理:

清理: 調用lint unUsedResources掃描規則生成備援資源的xml檔案,解析該結果xml檔案區分檔案格式和xml屬性格式的資源(資源格式見本文第一節),批量删除兩種不同格式的資源,并在執行路徑下生成備份路徑按res原路徑結構備份删除的内容,支援多次循環調用直至備援資源結果為0。

靜态測試技術之 Lint 備援資源清理

還原:将備份路徑下的檔案或xml屬性資源還原到原路徑,并自動添加到lint白名單。

靜态測試技術之 Lint 備援資源清理

2、自動監控:在持續內建平台上內建清理工具,輸出備援資源清理前後兩個安裝包及清理資源集,及時監控項目中的備援資源情況,也可以直覺看到清理帶來的優化效果,推動項目組在釋出前清理備援資源。

也附上在手管6.8.1版本代碼的清理結果:删除檔案資源950+個,其他各類型屬性2500+個,包大小縮小2.11M,預計在7.0頁面改版可以達到更好的應用效果。

靜态測試技術之 Lint 備援資源清理

備援資源清理是借助靜态代碼分析工具的一個小應用,大家在項目過程中是否有其他靜态分析工具應用的場景呢?

歡迎大家一起探讨。

參考資源:

[1] https://developer.android.com/guide/topics/resources/index.html;

[2] Android資源管理-drawable篇;

[3] http://tools.android.com/tips/lint/writing-a-lint-check;

[4] https://android.googlesource.com/platform/tools/base/+/master/lint;

[5] 微桌面Android資源清理工具。