概述
Lint 是 Android studio 提供的一款靜態(tài)代碼檢查工具圃庭,它可以幫助我們檢查 Android 項(xiàng)目源文件是否有潛在的 bug吏颖,以及在正確性、安全性季希、性能褪那、易用性、無(wú)障礙性和國(guó)際化方面是否需要優(yōu)化改進(jìn)式塌。Lint 的好處不言而喻博敬,它能夠在編碼階段就幫我們提前發(fā)現(xiàn)代碼中的“壞味道”,顯著降低線(xiàn)上問(wèn)題出現(xiàn)的概率峰尝;同時(shí)也能有效促進(jìn)團(tuán)隊(duì)的開(kāi)發(fā)規(guī)范的統(tǒng)一偏窝。
關(guān)于執(zhí)行 lint 檢查的幾種方式不多做贅述,接下來(lái)著重來(lái)看下如何實(shí)現(xiàn)自定義 Lint 規(guī)則并應(yīng)用到實(shí)際項(xiàng)目中武学。
自定義 Lint 接入方案
自定義 Lint 規(guī)則最終都會(huì)打成 JAR 包祭往,只需將該輸出 JAR 提供給其他組件使用即可。目前有兩種方式可供選擇:
全局方案
把此 jar 拷貝到 ~/.android/lint/
目錄中即可劳淆。缺點(diǎn)顯而易見(jiàn):針對(duì)所有工程生效,會(huì)影響同一臺(tái)機(jī)器其他工程的 Lint 檢查默赂。即便觸發(fā)工程時(shí)拷貝過(guò)去沛鸵,執(zhí)行完刪除,但其他進(jìn)程或線(xiàn)程使用 ./gradlew lint
仍可能會(huì)受到影響缆八。
AAR 殼方案
另一種實(shí)現(xiàn)方式是將 jar 置于一個(gè) aar 中曲掰,如果某個(gè)工程想要接入執(zhí)行自定義的 lint 規(guī)則,只需依賴(lài)這個(gè)發(fā)布后的 aar 即可奈辰,如此一來(lái)栏妖,新增的 lint 規(guī)則就可將影響范圍控制在單個(gè)項(xiàng)目?jī)?nèi)了。另外奖恰,該方案也是 Google 目前推薦的方式吊趾,aar 內(nèi)容也支持 lint.jar
條目:
AAR 文件的文件擴(kuò)展名為 .aar宛裕,Maven 工件類(lèi)型應(yīng)該也是 aar。此文件本身是一個(gè) zip 文件论泛。唯一的必需條目是 /AndroidManifest.xml揩尸。AAR 文件可能包含以下一個(gè)或多個(gè)可選條目:
xx.aar
|-/classes.jar
|-/res/
|-/R.txt
|-/public.txt
|-/assets/
|-/libs/name.jar
|-/jni/abi_name/name.so(其中 abi_name 是 Android 支持的 ABI 之一)
|-/proguard.txt
|-/lint.jar
|-/api.jar
|-/prefab/(用于導(dǎo)出原生庫(kù))
具體可參考 Android 官方對(duì)于 aar 的介紹:developer.android.com/studio/proj…
編寫(xiě)自定義 Lint 規(guī)則
接下來(lái)主要從以下幾個(gè)方面來(lái)介紹自定義 Lint 的開(kāi)發(fā)流程。
1. 創(chuàng)建 java-library & 配置 lint 依賴(lài)
自定義的 lint 規(guī)則最終輸出格式為 jar 包屁奏,所以我們只需要?jiǎng)?chuàng)建一個(gè) java-library 即可岩榆,build.gradle
配置如下:
lint-rules/build.gradle
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
// 官方提供的Lint相關(guān)API,并不穩(wěn)定坟瓢,每次AGP升級(jí)都可能會(huì)更改勇边,且并不是向下兼容的
compileOnly "com.android.tools.lint:lint-api:${rootProject.ext.lintVersion}"
// 目前Android中內(nèi)置的lint檢測(cè)規(guī)則
compileOnly "com.android.tools.lint:lint-checks:${rootProject.ext.lintVersion}"
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
testImplementation "junit:junit:4.13.2"
testImplementation "com.android.tools.lint:lint:$lintVersion"
testImplementation "com.android.tools.lint:lint-tests:$lintVersion"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
jar {
manifest {
// Only use the "-v2" key here if your checks have been updated to the
// new 3.0 APIs (including UAST)
attributes('Lint-Registry-V2': 'com.dorck.lint.rules.old.MyCustomIssueRegistry')
}
}
configurations {
lintJarOutput
}
dependencies {
lintJarOutput files(jar)
}
defaultTasks 'assemble'
配置期間如果發(fā)現(xiàn)如下問(wèn)題:
需要將 java 閉包中的 sourceCompatibility
和 targetCompatibility
改為 1.8。
此外折联,如果你創(chuàng)建 module 時(shí)選擇的是 kotlin 語(yǔ)言粒褒,還可能會(huì)遇到以下這個(gè)坑:
只需要將 kotlin 標(biāo)準(zhǔn)庫(kù)依賴(lài)方式改為 compileOnly 即可:
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
2. 編寫(xiě) lint-rules
平時(shí)經(jīng)常使用 kotlin 開(kāi)發(fā)項(xiàng)目的同學(xué)應(yīng)該都遇到過(guò)這種情況:一旦我們希望類(lèi) A 實(shí)現(xiàn)一個(gè)接口 B,那么通過(guò) AS 快捷鍵 option+ enter
選擇 implement members
后就會(huì)為我們的類(lèi) A 自動(dòng)實(shí)現(xiàn) B 中接口崭庸,并加了一堆 TODO 方法:
目前編碼環(huán)境并不會(huì)提示任何錯(cuò)誤怀浆,然而,如果我們粗心忘記去掉上面接口實(shí)現(xiàn)中的 TODO
方法怕享,一旦我們其他類(lèi)調(diào)用到這個(gè)類(lèi) SomethingNew
执赡,程序就立馬拋出一個(gè) NotImplementedError
異常。顯然函筋,如果前置靜態(tài)代碼檢查階段沒(méi)有攔住這個(gè)問(wèn)題進(jìn)而跑到了線(xiàn)上沙合,那么就只能祈禱別人不會(huì)去調(diào)用了,否則故障在所難免了跌帐。好了首懈,既然需求過(guò)來(lái)了,我就來(lái)嘗試通過(guò)自定義 Lint 幫助團(tuán)隊(duì)其他成員在編碼階段就發(fā)現(xiàn)問(wèn)題并強(qiáng)制處理谨敛。
首先究履,在上一步中,我們?cè)?lint-rules/build.gradle
中指定了自定義的 MyCustomIssueRegistry
脸狸,現(xiàn)在里面空空如也最仑,我們需要先創(chuàng)建一個(gè) Detector 用于檢測(cè) Standard.kt
中的 TODO()
方法:
@Suppress("UnstableApiUsage")
class KotlinTodoDetector : Detector(), Detector.UastScanner {
override fun getApplicableMethodNames(): List<String> {
return listOf("TODO")
}
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
println("KotlinTodoDetector >>> matched TODO in [${method.parent.containingFile.toString()}]")
if (context.evaluator.isMemberInClass(method, "kotlin.StandardKt__StandardKt")) {
val deleteFix = fix().name("Delete this TODO method")
.replace().all().with("").build()
context.report(
ISSUE,
context.getLocation(node),
"You must fix `TODO()` first.", deleteFix)
}
}
companion object {
private const val ISSUE_ID = "KotlinTodo"
val ISSUE = Issue.create(
ISSUE_ID,
"Detecting `TODO()` method from kotlin/Standard.kt.",
"""
You have unimplemented method or undo work marked by `TODO()`,
please implement it or remove dangerous TODO.
""",
category = Category.CORRECTNESS,
priority = 9,
severity = Severity.ERROR,
implementation = Implementation(KotlinTodoDetector::class.java, Scope.JAVA_FILE_SCOPE),
)
}
}
此處我們需要檢測(cè)的對(duì)象是 Java 源文件,這里只需要繼承自 Detector
并實(shí)現(xiàn) Detector.UastScanner
接口即可炊甲。當(dāng)然泥彤,我們也可以選擇按組合方式實(shí)現(xiàn)更多其他 Scanner,這取決于我們希望掃描的文件范圍卿啡。目前支持的掃描范圍有:
- UastScanner:掃描 Java 或者 kotlin 源文件
- ClassScanner:掃描字節(jié)碼或編譯的類(lèi)文件
- BinaryResourceScanner:掃描二進(jìn)制資源文件(res/raw/bitmap等)
- ResourceFolderScanner:掃描資源文件夾
- XmlScanner:掃描 xml 格式文件
- GradleScanner:掃描 Gradle 格式文件
- OtherFileScanner:其他類(lèi)型文件
檢測(cè) Java 源文件吟吝,可以通過(guò) getApplicableMethodNames
指定掃描的方法名,其他還有類(lèi)名颈娜、文件名剑逃、屬性名等等浙宜,并通過(guò) visitMethodCall
接受檢測(cè)到的方法。這里我們只需要檢測(cè) Kotlin 標(biāo)準(zhǔn)庫(kù)中的 Standard.kt
中的 TODO
方法炕贵,匹配到后通過(guò) context.report
來(lái)報(bào)告具體問(wèn)題梆奈,這里需要指定一個(gè) Issue 對(duì)象來(lái)描述問(wèn)題具體信息,相關(guān)字段如下:
- id : 唯一值称开,應(yīng)該能簡(jiǎn)短描述當(dāng)前問(wèn)題亩钟。利用 Java 注解或者 XML 屬性進(jìn)行屏蔽時(shí),使用的就是這個(gè) id鳖轰。
- summary : 簡(jiǎn)短的總結(jié)清酥,通常5-6個(gè)字符,描述問(wèn)題而不是修復(fù)措施蕴侣。
- explanation : 完整的問(wèn)題解釋和修復(fù)建議焰轻。
- category : 問(wèn)題類(lèi)別。常見(jiàn)的有:CORRECTNESS昆雀、SECURITY辱志、COMPLIANCE、USABILITY狞膘、LINT等等揩懒。
- priority : 優(yōu)先級(jí)。1-10 的數(shù)字挽封,10 為最重要/最嚴(yán)重已球。
- severity : 嚴(yán)重級(jí)別:Fatal, Error, Warning, Informational, Ignore。
- Implementation : 為 Issue 和 Detector 提供映射關(guān)系辅愿,Detector 就是當(dāng)前 Detector智亮。聲明掃描檢測(cè)的范圍Scope,Scope 用來(lái)描述 Detector 需要分析時(shí)需要考慮的文件集点待,包括:Resource文件或目錄阔蛉、Java文件、Class文件等癞埠。
此外状原,我們還可以設(shè)置出現(xiàn)該 issue 上報(bào)時(shí)的默認(rèn)解決方案 fix,這里我們創(chuàng)建了一個(gè) deleteFix
實(shí)現(xiàn)開(kāi)發(fā)者快速移除報(bào)錯(cuò)位置的 TODO
代碼燕差。
最后遭笋,只需要自定義一個(gè) Registry 聲明自己需要檢測(cè)的 Issues 即可:
@Suppress("UnstableApiUsage")
class MyCustomIssueRegistry : IssueRegistry() {
init {
println("MyCustomIssueRegistry, run...")
}
override val issues: List<Issue>
get() = listOf(
JcenterDetector.ISSUE,
KotlinTodoDetector.ISSUE,
)
override val minApi: Int
get() = 8 // works with Studio 4.1 or later; see com.android.tools.lint.detector.api.Api / ApiKt
override val api: Int
get() = CURRENT_API
override val vendor: Vendor
get() = Vendor(
vendorName = "Dorck",
contact = "xxx@gmail.com"
)
}
更多關(guān)于 AST 相關(guān)類(lèi)及語(yǔ)法介紹可參考官方指導(dǎo)文檔或者 Lint 源碼坝冕,此處不多做介紹徒探,這里很難一言以蔽之。
3. Lint 發(fā)布&接入
文章開(kāi)頭部分已經(jīng)介紹了 Lint 的相關(guān)接入方案喂窟,出于靈活性和可用性角度考慮自然選擇 aar 殼的方式测暗。經(jīng)過(guò)這幾年 lint 的發(fā)展央串,實(shí)現(xiàn)起來(lái)也很簡(jiǎn)單:只需要?jiǎng)?chuàng)建一個(gè) Android-Library module,然后稍微配置下 gradle 即可:
lint-aar:
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
dependencies {
lintPublish project(':checks')
// other dependencies
}
就是這么簡(jiǎn)單碗啄,此處的 lintPublish
配置允許我們引用另一個(gè) module质和,它會(huì)獲取該組件輸出的 jar 并將其打包為 lint.jar
然后放到自身的 AAR 中。
最后稚字,我們?cè)?app 模塊中依賴(lài)一下 lint-aar
這個(gè)組件饲宿,并編寫(xiě)以下測(cè)試代碼:
interface SimpleInterface {
fun initialize()
fun doSomething()
}
class SomethingNew : SimpleInterface {
override fun initialize() {
TODO("Not yet implemented")
}
override fun doSomething() {
TODO("Not yet implemented")
}
}
接下來(lái)執(zhí)行一下 ./gradlew :app:lint
即可看到控制臺(tái)輸出以下內(nèi)容:
我們也可以點(diǎn)擊 Lint 輸出的測(cè)試報(bào)告鏈接去查看詳細(xì)信息:
Note:AGP 7.0 開(kāi)始,執(zhí)行
./gradlew :app:lint
只會(huì)作用于默認(rèn)變體 lint 任務(wù)上胆描,而不是諸如此前的執(zhí)行所有變體 lint 任務(wù)瘫想。例如:我們此前執(zhí)行
./gradlew :app:lint
可能會(huì)導(dǎo)致debugLint
、releaseLint
昌讲、releaseChinaLint
等諸多變體 lint 任務(wù)的執(zhí)行国夜,嚴(yán)重拖慢了編譯速度,所以一般要指定特定變體的 lint 任務(wù)來(lái)執(zhí)行:./gradlew :app:lintDebug
短绸。而7.0開(kāi)始將無(wú)需如此麻煩车吹,盡管放心使用./gradlew :app:lint
即可。
最后醋闭,在 Android studio 中我們也可以看到編譯器給我們的代碼警告了:
并且我們上面設(shè)置的 deleteFix 也生效了窄驹,即點(diǎn)擊 Delete this TODO method
就可以輕松移除 TODO()
方法,快速解決問(wèn)題目尖。
4. 編寫(xiě)測(cè)試代碼
TTD(Test-Driven Development)是一個(gè)不錯(cuò)的習(xí)慣馒吴,很多時(shí)候作為開(kāi)發(fā)人員大多時(shí)候無(wú)需關(guān)心最新編寫(xiě)的 lint 組件發(fā)布狀態(tài),因?yàn)椴粩喟l(fā)布和集成到示例代碼中測(cè)試是一個(gè)比較糟糕的體驗(yàn)瑟曲,嚴(yán)重消耗我們的精力饮戳。如此一來(lái),我們就不得不了解下 lint 規(guī)則編碼時(shí)的單測(cè)流程了洞拨,我相信能夠顯著提升你的開(kāi)發(fā)效率扯罐。
首先,我們需要依賴(lài) Lint 的單測(cè)組件:
testImplementation "com.android.tools.lint:lint-tests:$lintVersion"
接著烦衣,在 lint-rules
模塊中創(chuàng)建單測(cè)文件用于驗(yàn)證我們之前的 KotlinTodo
規(guī)則:
最后來(lái)看下 KotlinTodoDetectorTest
如何實(shí)現(xiàn)的:
package com.dorck.lint.examples
import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import com.dorck.lint.rules.old.issues.KotlinTodoDetector
import org.junit.Test
@Suppress("UnstableApiUsage")
class KotlinTodoDetectorTest {
@Test
fun sampleTest() {
lint().files(
kotlin(
"""
package test.pkg
class SimpleInterfaceImpl : SimpleInterface {
override fun doSomething(){
TODO("Not yet implemented")
}
}
interface SimpleInterface {
fun doSomething()
}
""".trimIndent()
))
.issues(KotlinTodoDetector.ISSUE)
.run()
.expect(
"""
src/test/pkg/SimpleInterfaceImpl.kt:5: Error: You must fix TODO() first. [KotlinTodo]
TODO("Not yet implemented")
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
""".trimIndent()
)
.expectFixDiffs(
"""
Fix for src/test/pkg/SimpleInterfaceImpl.kt line 5: Delete this TODO method:
@@ -5 +5
- TODO("Not yet implemented")
""".trimIndent()
)
}
}
其實(shí)也很簡(jiǎn)單歹河,只需要模擬創(chuàng)建一個(gè) Java/kotlin/Gradle/xml 等格式的源文件,然后在 java()
或 koltin()
方法參數(shù)里面寫(xiě)上測(cè)試代碼花吟,并指定要驗(yàn)證的 Issue 以及期待的反饋內(nèi)容秸歧。當(dāng)然,expect()
中期待的輸出檢查結(jié)果我們是無(wú)法知曉的衅澈,我們只需要先設(shè)置為空字符串键菱,然后先跑一下測(cè)試用例,預(yù)期肯定會(huì)失敗今布,如此经备,我們只需要將終端輸出的實(shí)際錯(cuò)誤信息 copy 到 expect()
中即可:
最后拭抬,重新 run 一下單測(cè),就會(huì)發(fā)現(xiàn)能夠正常通過(guò)測(cè)試了侵蒙。更多關(guān)于 Lint 單元測(cè)試的用法可以參考:Lint unit testing
5. 忽略某些規(guī)則檢查
某些情況下造虎,我們希望忽略某些 Lint 規(guī)則的檢查或者更改 Lint 規(guī)則的嚴(yán)重級(jí)別,那么纷闺,我們可以選擇增加一個(gè) Lint 配置文件算凿,用于解決上述問(wèn)題。我們可以手動(dòng)在項(xiàng)目 app/根目錄下創(chuàng)建一個(gè)名為 lint.xml
的文件:
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- list of issues to configure -->
<issue id="DefaultLocale" severity="ignore"/>
<issue id="DeprecatedProvider" severity="ignore"/>
<issue id="ObsoleteLayoutParam">
<!-- The <ignore> tag has two possible attributes: path and regexp (see below) -->
<ignore path="res/layout-xlarge/activation.xml" />
<!-- You can use globbing patterns in the path strings -->
<ignore path="**/layout-x*/onclick.xml" />
<ignore path="res/**/activation.xml" />
</issue>
<issue id="MissingTranslation" severity="ignore"/>
<issue id="KotlinTodo" severity="ignore"/>
</lint>
在 lint.xml 中我們可以選擇更改某條規(guī)則的嚴(yán)重級(jí)別犁功,使原本不受重視的規(guī)則更加引人注意或者放寬其他規(guī)則的級(jí)別澎媒。當(dāng)然,我們也可以指定某條規(guī)則在特定匹配路徑下被忽略波桩,這將取決于我們自己設(shè)定的 regex 匹配規(guī)則戒努。
Note:如果我們創(chuàng)建了 lint.xml (文件名強(qiáng)約定),并且
build.gradle
的lintOptions
中沒(méi)有自定義設(shè)定 lint 配置文件的名稱(chēng)和路徑镐躲,則 AGP自動(dòng)在臨近目錄中尋找名為lint.xml
的配置文件储玫。詳細(xì)參考:Configure by lint xml
此外,我們也可以通過(guò)在 build.gradle
>> lintOptions
DSL 中設(shè)置開(kāi)啟或者關(guān)閉某些特定規(guī)則萤皂,當(dāng)然也可以配置報(bào)告的輸出格式以及路徑:
lintOptions {
textReport false
lintConfig file('default-lint.xml') // At `app/default-lint.xml`
disable 'KotlinTodo', 'MissingTranslation'
xmlOutput file("lint-report.xml")
}
更多關(guān)于 LintOptions
DSl 的配置可查看官方文檔:LintOptions-dsl
Note:如果你項(xiàng)目中使用了 lint plugin撒穷,那么可以參考 lint DSL的相關(guān)釋義:AGP-lint-dsl
其他的設(shè)置 lint 配置的方式還有手動(dòng)在 Android studio 的工具欄 Analyze > Inspect Code > Specify Inspection Scope 中或者通過(guò) Lint 命令行工具來(lái)配置,這兩種方式就不具體介紹了裆熙,感興趣的朋友可以去看下官方文檔的介紹端礼。
版本迭代過(guò)程
AGP 4.0開(kāi)始,Android studio 支持了獨(dú)立的 com.android.lint
插件入录,進(jìn)一步降低了自定義 lint 的成本蛤奥。借助此插件,在上述 lint-rules/build.gradle
中通過(guò)在 manifest 中注冊(cè)自定義 Registry
改為通過(guò)服務(wù)表單注冊(cè)(當(dāng)然僚稿,以前的方式目前還是可以用的)凡桥。以下是基于官方最新推薦的方式來(lái)配置和注冊(cè)自定義規(guī)則的:
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
id 'com.android.lint'
}
dependencies {
// 官方提供的Lint相關(guān)API,并不穩(wěn)定蚀同,每次AGP升級(jí)都可能會(huì)更改缅刽,且并不是向下兼容的
compileOnly "com.android.tools.lint:lint-api:${rootProject.ext.lintVersion}"
// 目前Android中內(nèi)置的lint檢測(cè)規(guī)則
compileOnly "com.android.tools.lint:lint-checks:${rootProject.ext.lintVersion}"
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
testImplementation "junit:junit:4.13.2"
testImplementation "com.android.tools.lint:lint:$lintVersion"
testImplementation "com.android.tools.lint:lint-tests:$lintVersion"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
可以看到,以此插件方式蠢络,我們需要關(guān)注的額外配置更少了衰猛,很大程度上降低了接入成本。
下面再來(lái)談?wù)?AGP-7.0 開(kāi)始的改動(dòng)刹孔。其一變動(dòng)是上面談及過(guò)的執(zhí)行 ./gradlew lint
只會(huì)作用于默認(rèn)變體的 Lint 任務(wù)上啡省,而不是以前的所有變體任務(wù)。
另外一項(xiàng)是在 7.0 中,lint 最終將能夠跨模塊增量運(yùn)行冕杠,這意味著如果我們只更改一個(gè)模塊中的代碼,lint 只需在該模塊下游的模塊上重新運(yùn)行分析檢測(cè)酸茴。對(duì)于具有許多模塊的大型項(xiàng)目分预,這應(yīng)該是一項(xiàng)重大的改進(jìn)。
開(kāi)發(fā)技巧
1. 借助 Psi 工具查看 AST 語(yǔ)法樹(shù)
Lint 檢查的實(shí)質(zhì)是對(duì)代碼的 AST(Abstract Syntax Tree薪捍,即抽象語(yǔ)法樹(shù))數(shù)據(jù)進(jìn)行檢查分析笼痹,故而會(huì)用到大量 AST 與 lombok.ast 開(kāi)源庫(kù)相關(guān)知識(shí)。閱讀源碼是一種不錯(cuò)的分析語(yǔ)法樹(shù)方式酪穿,不過(guò)我們可以借助 AS 的一些插件幫我們快速便捷解析類(lèi)的節(jié)點(diǎn)樹(shù)并加以解讀凳干。
利用 PsiViewer
就可以查看類(lèi)的 AST 構(gòu)造,如此一來(lái)我就可以另辟蹊徑找到特定的屬性來(lái)匹配特定代碼了被济。值得注意的是救赐,上面的 AST viewer 插件對(duì) kotlin 代碼支持不是很好,如果有需要只磷,建議先將 kotlin 反編譯為 java 再分析经磅。
2. 參考 Android 內(nèi)置 Lint 規(guī)則
我發(fā)現(xiàn)官方近期對(duì)于 Lint 的技術(shù)推進(jìn)很上心,各路文檔和 FAQ 陸續(xù)補(bǔ)齊了钮追。關(guān)于內(nèi)置規(guī)則预厌,Android官方團(tuán)隊(duì)也對(duì)每條做了詳細(xì)說(shuō)明和用法指導(dǎo),詳細(xì)參考:googlesamples.github.io/android-cus…
相關(guān)參考
- developer.android.google.cn/studio/writ…
- googlesamples/android-custom-lint-rules
- 美團(tuán)/Android自定義lint實(shí)踐
- android-custom-lint-user-guide
- Publishing a link check
- juejin.cn/post/686156…
最后
后續(xù)將持續(xù)更新lint實(shí)戰(zhàn)篇元媚,敬請(qǐng)期待轧叽。本文涉及相關(guān)源碼可以查看此處傳送:github.com/Moosphan/Mi…