Lint工具


一、Lint是什么

 Lint是ADT 16引入的靜態代碼檢測工具(位於sdk/tools/bin/lint ),可以對Android工程的源文件進行掃描,找出在正確性、安全性、性能、易用性、無障礙性以及國際化等方面可能存在的bug和可優化提升的地方。

 Lint默認包括幾百個檢測項,主要分為以下六類:

Valid issue categories:
    Correctness  //編碼錯誤或不完美,比如"WrongThread"表示可能在非UI線程調用widgets方法
    Security //不安全的編碼,比如“SetJavaScriptEnabled”表示WebView支持JavaScript
    Performance //對性能有影響的編碼,比如"Wakelock"表示未正確釋放屏幕鎖,導致功耗增加
    Usability //有更好的可用選項,比如"SmallSp"表示使用了小於12sp的尺寸
    Accessibility   //輔助選項,比如"ContentDescription"表示Image缺少ContentDescription屬性
    Internationalization  //國際化,比如"RtlEnabled"表示使用了RTL屬性但沒有在manifext文件中設置android:supportsRtl為true

可以通過lint --list命令查看所有支持的檢測項:

$lint --list

也可以通過lint --show  <id> 命令查看某一檢測項的詳細介紹:

$ lint --show RtlEnabled
RtlEnabled
----------
Summary: Using RTL attributes without enabling RTL support

Priority: 3 / 10
Severity: Warning
Category: Internationalization:Bidirectional Text

To enable right-to-left support, when running on API 17 and higher, you must
set the android:supportsRtl attribute in the manifest <application> element.

If you have started adding RTL attributes, but have not yet finished the
migration, you can set the attribute to false to satisfy this lint check.

 除此之外,google還提供了自定義Lint檢查規則的方法


 

二、如何啟動Lint檢測

使用Android Studio時,無論何時構建應用,都會自動運行配置的Lint和IDE檢查。但更多的時候,我們需要主動運行這些檢測,並獲取檢測報告。

我們有兩種方式啟動Lint檢測:

(1)命令行檢測

我們可以在項目根目錄下通過如下命令啟動Lint檢測:

./gradlew lint

檢測完畢后可以看到類似如下結果:

> Task :lockscreen:lint
Ran lint on variant debug: 335 issues found
Ran lint on variant release: 335 issues found
Wrote HTML report to file:///home/kevin/kevin/project/sysq/SystemUI/lockscreen/build/reports/lint-results.html
Wrote XML report to file:///home/kevin/kevin/project/sysq/SystemUI/lockscreen/build/reports/lint-results.xml

檢測報告被同時保存在lint-results.html和lint-results.xml兩個文件中,打開后可以看到每個問題的詳細描述和定位,以及修改建議:

    <issue
        id="WrongConstant"
        severity="Error"
        message="Must be one of: View.LAYER_TYPE_NONE, View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE"
        category="Correctness"
        priority="6"
        summary="Incorrect constant"
        explanation="Ensures that when parameter in a method only allows a specific set of constants, calls obey those rules."
        errorLine1="            ViewCompat.setLayerType(getChildAt(i), layerType, null);"
        errorLine2="                                                   ~~~~~~~~~">
        <location
            file="/home/kevin/kevin/project/sysq/SystemUI/lockscreen/src/main/java/com/lenovo/artlock/LeatherViewPager.java"
            line="1793"
            column="52"/>
    </issue>

注:lint在jdk 9 和 jdk 11會報錯,替換其他版本可以解決;另外,如果是window,則直接使用

gradlew lint

(2)手動運行檢測

以Android Studio為例:

1.打開一個工程,選擇要分析的項目、文件夾或文件

2.在菜單欄中依次選擇Analyze > Inspect Code

3.查看檢測范圍和配置文件

4.點擊ok以運行檢查

5.查看檢測結果

注:通過Custom scop可以自定義檢測范文,Inspection profile可以指定或修改內置的lint配置文件,下面會詳細介紹。


 

三、Lint配置文件

 如果希望忽略一些檢測項或調整其嚴重等級,可以通過修改配置文件lint.xml的方式。

Lint的工作流程如下:

修改配置文件同樣有兩種方式:

(1)手動創建

如果是手動創建lint.xml文件,則將其放在Android項目的根目錄下,其格式如下

    <?xml version="1.0" encoding="UTF-8"?>
    <lint>
        <!-- Disable the given check in this project -->
        <issue id="IconMissingDensityFolder" severity="ignore" />

        <!-- Ignore the ObsoleteLayoutParam issue in the specified files -->
        <issue id="ObsoleteLayoutParam">
            <ignore path="res/layout/activation.xml" />
            <ignore path="res/layout-xlarge/activation.xml" />
        </issue>

        <!-- Ignore the UselessLeaf issue in the specified file -->
        <issue id="UselessLeaf">
            <ignore path="res/layout/main.xml" />
        </issue>

        <!-- Change the severity of hardcoded strings to "error" -->
        <issue id="HardcodedText" severity="error" />
    </lint>
    

在lint.xml定義的id會覆蓋默認配置,未涉及的id則保持默認配置。

(2)Android Studio內置Lint配置文件

Android Studio內置了Lint和一些其他檢查配置文件,通過第二部分中提到的Inspection profile選項可以修改。

其中打√代表打開,Severity 代表嚴重等級,可以根據自己的需要修改。

Gradle中的lintOptions

除了上面修改單個issue的開關和嚴重等級,Gradle也提供了針對Lint運行方式的配置,可以在build.gradle中修改。

  android {
    // 移除lint檢查的error,可以避免由於編譯條件太過嚴格而編譯不過的問題
    lintOptions {
        // 如果為 true,則當lint發現錯誤時停止 gradle構建
        abortOnError false
        // 如果為 true,則只報告錯誤
        ignoreWarnings true
        // 不檢查給定的問題id InvalidPackage: Package not included in Android
        disable 'InvalidPackage'
        // 不檢查給定的問題id 資源類型錯誤
        disable "ResourceType"
        // 忽略因MissingTranslation導致Build Failed錯誤 "app_name"
        disable 'MissingTranslation'
        // 檢查給定的問題 id 
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' 
        // * 僅 * 檢查給定的問題 id 
        check 'NewApi', 'InlinedApi'
        // 配置寫入輸出結果的位置;它可以是一個文件或 “stdout”(標准輸出)
        textOutput 'stdout'
        // 如果為真,會生成一個XML報告,以給Jenkins之類的使用
        xmlReport false
        // 用於寫入報告的文件(如果不指定,默認為lint-results.xml)
        xmlOutput file("lint-report.xml")
        // 如果為真,會生成一個HTML報告(包括問題的解釋,存在此問題的源碼,等等)
        htmlReport true
        // 寫入報告的路徑,它是可選的(默認為構建目錄下的 lint-results.html )
        htmlOutput file("lint-report.html")
        // 設置為 true, 將使所有release 構建都以issus的嚴重性級別為fatal
        //(severity=false)的設置來運行lint
        // 並且,如果發現了致命(fatal)的問題,將會中止構建
        //(由上面提到的 abortOnError 控制)
        checkReleaseBuilds true
        // 設置給定問題的嚴重級別(severity)為fatal (這意味着他們將會
        // 在release構建的期間檢查 (即使 lint 要檢查的問題沒有包含在代碼中)
        fatal 'NewApi', 'InlineApi'
        // 設置給定問題的嚴重級別為error
        error 'Wakelock', 'TextViewEdits'
        // 設置給定問題的嚴重級別為warning
        warning 'ResourceAsColor'
        // 設置給定問題的嚴重級別(severity)為ignore (和不檢查這個問題一樣)
        ignore 'TypographyQuotes'
        // 如果為 true,則檢查所有的問題,包括默認不檢查問題
        checkAllWarnings true
        // 重置 lint 配置(使用默認的嚴重性等設置)。
        lintConfig file("default-lint.xml")
        // 設置為 true,則當有錯誤時會顯示文件的全路徑或絕對路徑 (默認情況下為true)
        absolutePaths true
    }

}

注:如果只想對某以特定問題進行檢查,可以使用check命令,check命令會覆蓋disable和enable命令;同時abortOnError設置為false是必要的,這樣可以防止lint檢查提前結束。


 

四、常見問題處理

首先我們需要知道,規則是死的,代碼是活的,並非所有的Lint警告都需要通過修改代碼來解決,有些警告可以被當成善意的提醒,我們可以通過一些標注來忽略它。

如果是java代碼,可以通過注解@SuppressLint("警告名稱"),如

@SuppressLint(“HandlerLeak”)

@SuppressLint(“all”)  //如果不清楚警告名稱,可以直接忽略all

如果是xml文件,可以通過屬性tools:ignore="警告名稱",如

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="TextFields,HardcodedText,UselessParent">

<!--如果不清楚警告名稱,可以直接忽略all --> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="all">

 

下面列舉一些常見的警告,以及推薦的處理方式:

(1)[Accessibility] Missing contentDescription attribute on image

原因:為了照顧一些視力不好的小伙伴,我們在使用Image相關組件(如ImageView或ImageButton)時,需要添加contentDescription屬性,以便通過聲音等途徑告知圖片中的內容。

方案:添加contentDescription屬性

(2)To get local formatting use getDateInstance(), getDateTimeInstance(), or getTimeInstance(), or use new SimpleDateFormat(String template, Locale locale) with for example Locale.US for ASCII dates.

原因:一般是new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")未添加第二個參數Locale造成,如果希望獲取的格式化時間保持一致,可以使用如Locale.CHINA;或者希望與本地保持一致,則使用Locale.getDefault().
方案:添加第二個參數Locale

(3)Consider using apply() instead; commit writes its data to persistent storage immediately, whereas apply will handle it in the background

原因:在寫入SharedPreferences數據時,commit()會立刻寫入持久化數據,而apply()會在后台適當的時候異步寫入,顯然如果不是必須,后者效率更高。
方案:將commit()替換為apply()

(4)This <FrameLayout> can be replaced with a <merge> tag

原因:如果FrameLayout是根布局,並且不提供背景或填充等,它通常可以替換為稍微更有效的<merge>標簽。
方案:使用<merge>標簽代替<FrameLayout>

(5)This Handler class should be static or leaks might occur (anonymous android.os.Handler)

原因:Handler 類應該應該為static類型,否則有可能造成泄露。在程序消息隊列中排隊的消息保持了對目標Handler類的應用。如果Handler是個內部類,那么它也會保持它所在的外部類的引用,造成外部類內存泄露。
方案:給Handler加上static屬性,如果你需要在Handler中調用外部Activity的方法,就讓Handler持有一個對Activity的WeakReference,這樣就不會造成Activity的泄漏。

(6)Use new SparseIntArray(...) instead for better performance

原因:SparseArray是android里為<Interger,Object>這樣的Hashmap而專門寫的類,目的是提高內存效率,其核心是折半查找函數(binarySearch)。
方案:將<Integer, Object>替換為對應的SparseArray,如用SparseBooleanArray取代HashMap<Integer, Boolean>,用SparseIntArray取代HashMap<Integer, Integer>等。

(7)Use a layout_height of 0dp instead of fill_parent for better performance

原因:如果一個線性布局,中間的子布局設置了Android:layout_weight="1.0"屬性,會將剩余空間填充滿,這時候子布局的android:layout_width="fill_parent"就沒有用了。
方案:將android:layout_width="fill_parent"替換為android:layout_width="0dp",這樣可以加速layout計算。

(8)This Cursor should be freed up after use with #close()

原因:Cursor使用完未釋放
方案:釋放

(9)This tag and its children can be replaced by one <TextView/> and a compound drawable

原因:可以直接給TextView加圖片,不需要用的ImageView這個控件。
方案:使用android:drawableLeft.、android:drawableRight等屬性直接給TextView指定圖片。

(10)android:singleLine is deprecated: Use maxLines="1" instead

原因:android:singleLine 過期了
方案:改用android:maxLines

(11)Must be one of: Context.POWER_SERVICE, Context.WINDOW_SERVICE, Context.LAYOUT_INFLATER_SERVICE, Context.ACCOUNT_SERVICE, Context.ACTIVITY_SERVICE

原因:可能使用了魔法文字,如
.getSystemService("window");

方案:改為常量

.getSystemService(Context.WINDOW_SERVICE);

(12)@id/message_name can overlap @id/cancel_button if @id/message_name grows due to localized text expansion

原因:一般出現在RelativeLayout中,一個布局可能會覆蓋另一個布局,比如隨着TextView文本的增加,可能覆蓋其他控件。
方案:設置TextView的最大字符個數
android:maxEms="6"

(13)the resource 'R.color.aqua' appears to be unused

原因:無用資源

方案:刪除

(14)Avoid object allocations during draw/layout operations (preallocate and reuse instead)

原因:在draw和layout和onMeasure方法中最好不要創建對象,因為這些方法在一次顯示過程中會根據父布局的需要不止一次的調用。

方案:將其new方法提取出來。

(15)Avoid using sizes smaller than 12sp: 8sp

原因:避免使用尺寸小於12 sp:8 sp,其實就是想告訴你小於12sp真的太小啦,系統建議你改成12sp,因為他覺得12sp已經很小了,不建議比12sp還小

方案:不要小於12sp

(16)Field can be converted to a local variable

原因:字段可以轉換為一個局部變量

方案:轉換為一個局部變量

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM