美团外卖Android Lint代码检查实践
概述
Lint是Google提供的Android靜態(tài)代碼檢查工具,可以掃描并發(fā)現(xiàn)代碼中潛在的問(wèn)題,提醒開(kāi)發(fā)人員及早修正,提高代碼質(zhì)量。除了Android原生提供的幾百個(gè)Lint規(guī)則,還可以開(kāi)發(fā)自定義Lint規(guī)則以滿足實(shí)際需要。
為什么要使用Lint
在美團(tuán)外賣Android App的迭代過(guò)程中,線上問(wèn)題頻繁發(fā)生。開(kāi)發(fā)時(shí)很容易寫(xiě)出一些問(wèn)題代碼,例如Serializable的使用:實(shí)現(xiàn)了Serializable接口的類,如果其成員變量引用的對(duì)象沒(méi)有實(shí)現(xiàn)Serializable接口,序列化時(shí)就會(huì)Crash。我們對(duì)一些常見(jiàn)問(wèn)題的原因和解決方法做分析總結(jié),并在開(kāi)發(fā)人員組內(nèi)或跟測(cè)試人員一起分享交流,幫助相關(guān)人員主動(dòng)避免這些問(wèn)題。
為了進(jìn)一步減少問(wèn)題發(fā)生,我們逐步完善了一些規(guī)范,包括制定代碼規(guī)范,加強(qiáng)代碼Review,完善測(cè)試流程等。但這些措施仍然存在各種不足,包括代碼規(guī)范難以實(shí)施,溝通成本高,特別是開(kāi)發(fā)人員變動(dòng)頻繁導(dǎo)致反復(fù)溝通等,因此其效果有限,相似問(wèn)題仍然不時(shí)發(fā)生。另一方面,越來(lái)越多的總結(jié)、規(guī)范文檔,對(duì)于組內(nèi)新人也產(chǎn)生了不小的學(xué)習(xí)壓力。
有沒(méi)有辦法從技術(shù)角度減少或減輕上述問(wèn)題呢?
我們調(diào)研發(fā)現(xiàn),靜態(tài)代碼檢查是一個(gè)很好的思路。靜態(tài)代碼檢查框架有很多種,例如FindBugs、PMD、Coverity,主要用于檢查Java源文件或class文件;再例如Checkstyle,主要關(guān)注代碼風(fēng)格;但我們最終選擇從Lint框架入手,因?yàn)樗兄T多優(yōu)勢(shì):
在對(duì)Lint進(jìn)行了充分的技術(shù)調(diào)研后,我們根據(jù)實(shí)際遇到的問(wèn)題,又做了一些更深入的思考,包括應(yīng)該用Lint解決哪些問(wèn)題,怎么樣更好的推廣實(shí)施等,逐步形成了一套較為全面有效的方案。
Lint API簡(jiǎn)介
為了方便后文的理解,我們先簡(jiǎn)單看一下Lint提供的主要API。
主要API
Lint規(guī)則通過(guò)調(diào)用Lint API實(shí)現(xiàn),其中最主要的幾個(gè)API如下:
舉例來(lái)說(shuō),原生的ShowToast就是一個(gè)Issue,該規(guī)則檢查調(diào)用Toast.makeText()方法后是否漏掉了Toast.show()的調(diào)用。其Detector為ToastDetector,要檢查的Scope為JAVA_FILE_SCOPE,ToastDetector實(shí)現(xiàn)了JavaPsiScanner,示意代碼如下:
public class ToastDetector extends Detector implements JavaPsiScanner {public static final Issue ISSUE = Issue.create("ShowToast","Toast created but not shown","...",Category.CORRECTNESS,6,Severity.WARNING,new Implementation(ToastDetector.class,Scope.JAVA_FILE_SCOPE));// ... }IssueRegistry的示意代碼如下:
public class MyIssueRegistry extends IssueRegistry {@Overridepublic List<Issue> getIssues() {return Arrays.asList(ToastDetector.ISSUE,LogDetector.ISSUE,// ...);} }Scanner
Lint開(kāi)發(fā)過(guò)程中最主要的工作就是實(shí)現(xiàn)Scanner。Lint中包括多種類型的Scanner如下,其中最常用的是掃描Java源文件和XML文件的Scanner。
- JavaScanner / JavaPsiScanner / UastScanner:掃描Java源文件
- XmlScanner:掃描XML文件
- ClassScanner:掃描class文件
- BinaryResourceScanner:掃描二進(jìn)制資源文件
- ResourceFolderScanner:掃描資源文件夾
- GradleScanner:掃描Gradle腳本
- OtherFileScanner:掃描其他類型文件
值得注意的是,掃描Java源文件的Scanner先后經(jīng)歷了三個(gè)版本。
本文目前仍然基于PsiJavaScanner做介紹。根據(jù)UastScanner源碼中的注釋,可以很容易的從PsiJavaScanner遷移到UastScanner。
Lint規(guī)則
我們需要用Lint檢查代碼中的哪些問(wèn)題呢?
開(kāi)發(fā)過(guò)程中,我們比較關(guān)注App的Crash、Bug率等指標(biāo)。通過(guò)長(zhǎng)期的整理總結(jié)發(fā)現(xiàn),有不少發(fā)生頻率很高的代碼問(wèn)題,其原理和解決方案都很明確,但是在寫(xiě)代碼時(shí)卻很容易遺漏且難以發(fā)現(xiàn);而Lint恰好很容易檢查出這些問(wèn)題。
Crash預(yù)防
Crash率是App最重要的指標(biāo)之一,避免Crash也一直是開(kāi)發(fā)過(guò)程中比較頭疼的一個(gè)問(wèn)題,Lint可以很好的檢查出一些潛在的Crash。例如:
- 原生的NewApi,用于檢查代碼中是否調(diào)用了Android高版本才提供的API。在低版本設(shè)備中調(diào)用高版本API會(huì)導(dǎo)致Crash。
- 自定義的SerializableCheck。實(shí)現(xiàn)了Serializable接口的類,如果其成員變量引用的對(duì)象沒(méi)有實(shí)現(xiàn)Serializable接口,序列化時(shí)就會(huì)Crash。我們制定了一條代碼規(guī)范,要求實(shí)現(xiàn)了Serializable接口的類,其成員變量(包括從父類繼承的)所聲明的類型都要實(shí)現(xiàn)Serializable接口。
- 自定義的ParseColorCheck。調(diào)用Color.parseColor()方法解析后臺(tái)下發(fā)的顏色時(shí),顏色字符串格式不正確會(huì)導(dǎo)致IllegalArgumentException,我們要求調(diào)用這個(gè)方法時(shí)必須處理該異常。
Bug預(yù)防
有些Bug可以通過(guò)Lint檢查來(lái)預(yù)防。例如:
- SpUsage:要求所有SharedPrefrence讀寫(xiě)操作使用基礎(chǔ)工具類,工具類中會(huì)做各種異常處理;同時(shí)定義SPConstants常量類,所有SP的Key都要在這個(gè)類定義,避免在代碼中分散定義的Key之間沖突。
- ImageViewUsage:檢查ImageView有沒(méi)有設(shè)置ScaleType,加載時(shí)有沒(méi)有設(shè)置Placeholder。
- TodoCheck:檢查代碼中是否還有TODO沒(méi)完成。例如開(kāi)發(fā)時(shí)可能會(huì)在代碼中寫(xiě)一些假數(shù)據(jù),但最終上線時(shí)要確保刪除這些代碼。這種檢查項(xiàng)比較特殊,通常在開(kāi)發(fā)完成后提測(cè)階段才檢查。
性能/安全問(wèn)題
一些性能、安全相關(guān)問(wèn)題可以使用Lint分析。例如:
- ThreadConstruction:禁止直接使用new Thread()創(chuàng)建線程(線程池除外),而需要使用統(tǒng)一的工具類在公用線程池執(zhí)行后臺(tái)操作。
- LogUsage:禁止直接使用android.util.Log,必須使用統(tǒng)一工具類。工具類中可以控制Release包不輸出Log,提高性能,也避免發(fā)生安全問(wèn)題。
代碼規(guī)范
除了代碼風(fēng)格方面的約束,代碼規(guī)范更多的是用于減少或防止發(fā)生Bug、Crash、性能、安全等問(wèn)題。很多問(wèn)題在技術(shù)上難以直接檢查,我們通過(guò)封裝統(tǒng)一的基礎(chǔ)庫(kù)、制定代碼規(guī)范的方式間接解決,而Lint檢查則用于減少組內(nèi)溝通成本、新人學(xué)習(xí)成本,并確保代碼規(guī)范的落實(shí)。例如:
- 前面提到的SpUsage、ThreadConstruction、LogUsage等。
- ResourceNaming:資源文件命名規(guī)范,防止不同模塊之間的資源文件名沖突。
代碼檢查的實(shí)施
當(dāng)檢查出代碼問(wèn)題時(shí),如何提醒開(kāi)發(fā)者及時(shí)修正呢?
早期我們將靜態(tài)代碼檢查配置在Jenkins上,打包發(fā)布AAR/APK時(shí),檢查代碼中的問(wèn)題并生成報(bào)告。后來(lái)發(fā)現(xiàn)雖然靜態(tài)代碼檢查能找出來(lái)不少問(wèn)題,但是很少有人主動(dòng)去看報(bào)告,特別是報(bào)告中還有過(guò)多無(wú)關(guān)緊要的、優(yōu)先級(jí)很低的問(wèn)題(例如過(guò)于嚴(yán)格的代碼風(fēng)格約束)。
因此,一方面要確定檢查哪些問(wèn)題,另一方面,何時(shí)、通過(guò)什么樣的技術(shù)手段來(lái)執(zhí)行代碼檢查也很重要。我們結(jié)合技術(shù)實(shí)現(xiàn),對(duì)此做了更多思考,確定了靜態(tài)代碼檢查實(shí)施過(guò)程中的主要目標(biāo):
優(yōu)先級(jí)定義
每個(gè)Lint規(guī)則都可以配置Sevirity(優(yōu)先級(jí)),包括Fatal、Error、Warning、Information等,我們主要使用Error和Warning,如下。
- Error級(jí)別:明確需要解決的問(wèn)題,包括Crash、明確的Bug、嚴(yán)重性能問(wèn)題、不符合代碼規(guī)范等,必須修復(fù)。
- Warning級(jí)別:包括代碼編寫(xiě)建議、可能存在的Bug、一些性能優(yōu)化等,適當(dāng)放松要求。
執(zhí)行時(shí)機(jī)
Lint檢查可以在多個(gè)階段執(zhí)行,包括在本地手動(dòng)檢查、編碼實(shí)時(shí)檢查、編譯時(shí)檢查、commit檢查,以及在CI系統(tǒng)中提Pull Request時(shí)檢查、打包發(fā)版時(shí)檢查等,下面分別介紹。
手動(dòng)執(zhí)行
在Android Studio中,自定義Lint可以通過(guò)Inspections功能(Analyze - Inspect Code)手動(dòng)運(yùn)行。
在Gradle命令行環(huán)境下,可直接用./gradlew lint執(zhí)行Lint檢查。
手動(dòng)執(zhí)行簡(jiǎn)單易用,但缺乏強(qiáng)制性,容易被開(kāi)發(fā)者遺漏。
編碼階段實(shí)時(shí)檢查
編碼時(shí)檢查即在Android Studio中寫(xiě)代碼時(shí)在代碼窗口實(shí)時(shí)報(bào)錯(cuò)。其好處很明顯,開(kāi)發(fā)者可以第一時(shí)間發(fā)現(xiàn)代碼問(wèn)題。但受限于Android Studio對(duì)自定義Lint的支持不完善,開(kāi)發(fā)人員IDE的配置不同,需要開(kāi)發(fā)者主動(dòng)關(guān)注報(bào)錯(cuò)并修復(fù),這種方式不能完全保證效果。
IDEA提供了Inspections功能和相應(yīng)的API來(lái)實(shí)現(xiàn)代碼檢查,Android原生Lint就是通過(guò)Inspections集成到了Android Studio中。對(duì)于自定義Lint規(guī)則,官方似乎沒(méi)有給出明確說(shuō)明,但實(shí)際研究發(fā)現(xiàn),在Android Studio 2.2+版本和基于JavaPsiScanner開(kāi)發(fā)的條件下(或Android Studio 3.0+和JavaPsiScanner/UastScanner),IDE會(huì)嘗試加載并實(shí)時(shí)執(zhí)行自定義Lint規(guī)則。
技術(shù)細(xì)節(jié):
在Android Studio 2.x版本中,菜單Preferences - Editor - Inspections - Android - Lint - Correctness - Error from Custom Lint Check(avaliable for Analyze|Inspect Code)中指出,自定義Lint只支持命令行或手動(dòng)運(yùn)行,不支持實(shí)時(shí)檢查。
Error from Custom Rule When custom (third-party) lint rules are integrated in the IDE, they are not available as native IDE inspections, so the explanation text (which must be statically registered by a plugin) is not available. As a workaround, run the lint target in Gradle instead; the HTML report will include full explanations.
在Android Studio 3.x版本中,打開(kāi)Android工程源碼后,IDE會(huì)加載工程中的自定義Lint規(guī)則,在設(shè)置菜單的Inspections列表里可以查看,和原生Lint效果相同(Android Studio會(huì)在打開(kāi)源文件時(shí)觸發(fā)對(duì)該文件的代碼檢查)。
分析自定義Lint的IssueRegistry.getIssues()方法調(diào)用堆棧,可以看到Android Studio環(huán)境下,是由org.jetbrains.android.inspections.lint.AndroidLintExternalAnnotator調(diào)用LintDriver加載執(zhí)行自定義Lint規(guī)則。
參考代碼: https://github.com/JetBrains/android/tree/master/android/src/org/jetbrains/android/inspections/lint
在Android Studio中的實(shí)際效果如圖:
本地編譯時(shí)自動(dòng)檢查
配置Gradle腳本可實(shí)現(xiàn)編譯Android工程時(shí)執(zhí)行Lint檢查。好處是既可以盡早發(fā)現(xiàn)問(wèn)題,又可以有強(qiáng)制性;缺點(diǎn)是對(duì)編譯速度有一定的影響。
編譯Android工程執(zhí)行的是assemble任務(wù),讓assemble依賴lint任務(wù),即可在編譯時(shí)執(zhí)行Lint檢查;同時(shí)配置LintOptions,發(fā)現(xiàn)Error級(jí)別問(wèn)題時(shí)中斷編譯。
在Android Application工程(APK)中配置如下,Android Library工程(AAR)把a(bǔ)pplicationVariants換成libraryVariants即可。
android.applicationVariants.all { variant ->variant.outputs.each { output ->def lintTask = tasks["lint${variant.name.capitalize()}"]output.assemble.dependsOn lintTask} }LintOptions的配置:
android.lintOptions {abortOnError true }本地commit時(shí)檢查
利用git pre-commit hook,可以在本地commit代碼前執(zhí)行Lint檢查,檢查不通過(guò)則無(wú)法提交代碼。這種方式的優(yōu)勢(shì)在于不影響開(kāi)發(fā)時(shí)的編譯速度,但發(fā)現(xiàn)問(wèn)題相對(duì)滯后。
技術(shù)實(shí)現(xiàn)方面,可以編寫(xiě)Gradle腳本,在每次同步工程時(shí)自動(dòng)將hook腳本從工程拷貝到.git/hooks/文件夾下。
提代碼時(shí)CI檢查
作為代碼提交流程規(guī)范的一部分,發(fā)Pull Request提代碼時(shí)用CI系統(tǒng)檢查L(zhǎng)int問(wèn)題是一個(gè)常見(jiàn)、可行、有效的思路??膳渲肅I檢查通過(guò)后代碼才能被合并。
CI系統(tǒng)常用Jenkins,如果使用Stash做代碼管理,可以在Stash上配置Pull Request Notifier for Stash插件,或在Jenkins上配置Stash Pull Request Builder插件,實(shí)現(xiàn)發(fā)Pull Request時(shí)觸發(fā)Jenkins執(zhí)行Lint檢查的Job。
在本地編譯和CI系統(tǒng)中做代碼檢查,都可以通過(guò)執(zhí)行Gradle的Lint任務(wù)實(shí)現(xiàn)??梢栽贑I環(huán)境下給Gradle傳遞一個(gè)StartParameter,Gradle腳本中如果讀取到這個(gè)參數(shù),則配置LintOptions檢查所有Lint問(wèn)題;否則在本地編譯環(huán)境下只檢查部分高優(yōu)先級(jí)Lint問(wèn)題,減少對(duì)本地編譯速度的影響。
Lint生成報(bào)告的效果如圖所示:
打包發(fā)布時(shí)檢查
即使每次提代碼時(shí)用CI系統(tǒng)執(zhí)行Lint檢查,仍然不能保證所有人的代碼合并后一定沒(méi)有問(wèn)題;另外對(duì)于一些特殊的Lint規(guī)則,例如前面提到的TodoCheck,還希望在更晚的時(shí)候檢查。
于是在CI系統(tǒng)打包發(fā)布APK/AAR用于測(cè)試或發(fā)版時(shí),還需要對(duì)所有代碼再做一次Lint檢查。
最終確定的檢查時(shí)機(jī)
綜合考慮多種檢查方式的優(yōu)缺點(diǎn)以及我們的目標(biāo),最終確定結(jié)合以下幾種方式做代碼檢查:
配置文件支持
為了方便代碼管理,我們給自定義Lint創(chuàng)建了一個(gè)獨(dú)立的工程,該工程打包生成一個(gè)AAR發(fā)布到Maven倉(cāng)庫(kù),而被檢查的Android工程依賴這個(gè)AAR(具體開(kāi)發(fā)過(guò)程可以參考文章末尾鏈接)。
自定義Lint雖然在獨(dú)立工程中,但和被檢查的Android工程中的代碼規(guī)范、基礎(chǔ)組件等存在較多耦合。
例如我們使用正則表達(dá)式檢查Android工程的資源文件命名規(guī)范,每次業(yè)務(wù)邏輯變動(dòng)要新增資源文件前綴時(shí),都要修改Lint工程,發(fā)布新的AAR,再更新到Android工程中,非常繁瑣。另一方面,我們的Lint工程除了在外賣C端Android工程中使用,也希望能直接用在其他端的其他Android工程中,而不同工程之間存在差異。
于是我們嘗試使用配置文件來(lái)解決這一問(wèn)題。以檢查L(zhǎng)og使用的LogUsage為例,不同工程封裝了不同的Log工具類,報(bào)錯(cuò)時(shí)提示信息也應(yīng)該不一樣。定義配置文件名為custom-lint-config.json,放在被檢查Android工程的模塊目錄下。在Android工程A中的配置文件是:
{"log-usage-message": "請(qǐng)勿使用android.util.Log,建議使用LogUtils工具類" }而Android工程B的配置文件是:
{"log-usage-message": "請(qǐng)勿使用android.util.Log,建議使用Logger工具類" }從Lint的Context對(duì)象可獲取被檢查工程目錄從而讀取配置文件,關(guān)鍵代碼如下:
import com.android.tools.lint.detector.api.Context;public final class LintConfig {private LintConfig(Context context) {File projectDir = context.getProject().getDir();File configFile = new File(projectDir, "custom-lint-config.json");if (configFile.exists() && configFile.isFile()) {// 讀取配置文件...}} }配置文件的讀取,可以在Detector的beforeCheckProject、beforeCheckLibraryProject回調(diào)方法中進(jìn)行。LogUsage中檢查到錯(cuò)誤時(shí),根據(jù)配置文件定義的信息報(bào)錯(cuò)。
public class LogUsageDetector extends Detector implements Detector.JavaPsiScanner {// ...private LintConfig mLintConfig;@Overridepublic void beforeCheckProject(@NonNull Context context) {// 讀取配置mLintConfig = new LintConfig(context);}@Overridepublic void beforeCheckLibraryProject(@NonNull Context context) {// 讀取配置mLintConfig = new LintConfig(context);}@Overridepublic List<String> getApplicableMethodNames() {return Arrays.asList("v", "d", "i", "w", "e", "wtf");}@Overridepublic void visitMethod(JavaContext context, JavaElementVisitor visitor, PsiMethodCallExpression call, PsiMethod method) {if (context.getEvaluator().isMemberInClass(method, "android.util.Log")) {// 從配置文件獲取MessageString msg = mLintConfig.getConfig("log-usage-message");context.report(ISSUE, call, context.getLocation(call.getMethodExpression()), msg);}} }模板Lint規(guī)則
Lint規(guī)則開(kāi)發(fā)過(guò)程中,我們發(fā)現(xiàn)了一系列相似的需求:封裝了基礎(chǔ)工具類,希望大家都用起來(lái);某個(gè)方法很容易拋出RuntimeException,有必要做處理,但Java語(yǔ)法上RuntimeException并不強(qiáng)制要求處理從而經(jīng)常遺漏……
這些相似的需求,每次在Lint工程中開(kāi)發(fā)同樣會(huì)很繁瑣。我們嘗試實(shí)現(xiàn)了幾個(gè)模板,可以直接在Android工程中通過(guò)配置文件配置Lint規(guī)則。
如下為一個(gè)配置文件示例:
{"lint-rules": {"deprecated-api": [{"method-regex": "android\\.content\\.Intent\\.get(IntExtra|StringExtra|BooleanExtra|LongExtra|LongArrayExtra|StringArrayListExtra|SerializableExtra|ParcelableArrayListExtra).*","message": "避免直接調(diào)用Intent.getXx()方法,特殊機(jī)型可能發(fā)生Crash,建議使用IntentUtils","severity": "error"},{"field": "java.lang.System.out","message": "請(qǐng)勿直接使用System.out,應(yīng)該使用LogUtils","severity": "error"},{"construction": "java.lang.Thread","message": "避免單獨(dú)創(chuàng)建Thread執(zhí)行后臺(tái)任務(wù),存在性能問(wèn)題,建議使用AsyncTask","severity": "warning"},{"super-class": "android.widget.BaseAdapter","message": "避免直接使用BaseAdapter,應(yīng)該使用統(tǒng)一封裝的BaseListAdapter","severity": "warning"}],"handle-exception": [{"method": "android.graphics.Color.parseColor","exception": "java.lang.IllegalArgumentException","message": "Color.parseColor需要加try-catch處理IllegalArgumentException異常","severity": "error"}]} }示例配置中定義了兩種類型的模板規(guī)則:
- DeprecatedApi:禁止直接調(diào)用指定API
- HandleException:調(diào)用指定API時(shí),需要加try-catch處理指定類型的異常
問(wèn)題API的匹配,包括方法調(diào)用(method)、成員變量引用(field)、構(gòu)造函數(shù)(construction)、繼承(super-class)等類型;匹配字符串支持glob語(yǔ)法或正則表達(dá)式(和lint.xml中ignore的配置語(yǔ)法一致)。
實(shí)現(xiàn)方面,主要是遍歷Java語(yǔ)法樹(shù)中特定類型的節(jié)點(diǎn)并轉(zhuǎn)換成完整字符串(例如方法調(diào)用android.content.Intent.getIntExtra),然后檢查是否有模板規(guī)則與其匹配。匹配成功后,DeprecatedApi規(guī)則直接輸出message報(bào)錯(cuò);HandleException規(guī)則會(huì)檢查匹配到的節(jié)點(diǎn)是否處理了特定Exception(或Exception的父類),沒(méi)有處理則報(bào)錯(cuò)。
按Git版本檢查新增文件
隨著Lint新規(guī)則的不斷開(kāi)發(fā),我們又遇到了一個(gè)問(wèn)題。Android工程中存在大量歷史代碼,不符合新增Lint規(guī)則的要求,但也沒(méi)有導(dǎo)致明顯問(wèn)題,這時(shí)接入新增Lint規(guī)則要求修改所有歷史代碼,成本較高而且有一定風(fēng)險(xiǎn)。例如新增代碼規(guī)范,要求使用統(tǒng)一的線程工具類而不允許直接用Handler以避免內(nèi)存泄露等。
我們嘗試了一個(gè)折中的方案:只檢查指定git commit之后新增的文件。在配置文件中添加配置項(xiàng),給Lint規(guī)則配置git-base屬性,其值為commit ID,只檢查此次commit之后新增的文件。
實(shí)現(xiàn)方面,執(zhí)行g(shù)it rev-parse --show-toplevel命令獲取git工程根目錄的路徑;執(zhí)行g(shù)it ls-tree --full-tree --full-name --name-only -r <commit-id>命令獲取指定commit時(shí)已有文件列表(相對(duì)git根目錄的路徑)。在Scanner回調(diào)方法中通過(guò)Context.getLocation(node).getFile()獲取節(jié)點(diǎn)所在文件,結(jié)合git文件列表判斷是否需要檢查這個(gè)節(jié)點(diǎn)。需要注意的是,代碼量較大時(shí)要考慮Lint檢查對(duì)電腦的性能消耗。
總結(jié)
經(jīng)過(guò)一段時(shí)間的實(shí)踐發(fā)現(xiàn),Lint靜態(tài)代碼檢查在解決特定問(wèn)題時(shí)的效果非常好,例如發(fā)現(xiàn)一些語(yǔ)言或API層面比較明確的低級(jí)錯(cuò)誤、幫助進(jìn)行代碼規(guī)范的約束。使用Lint前,不少這類問(wèn)題恰好對(duì)開(kāi)發(fā)人員來(lái)說(shuō)又很容易遺漏(例如原生的NewApi檢查、自定義的SerializableCheck);相同問(wèn)題反復(fù)出現(xiàn);代碼規(guī)范的執(zhí)行,特別是有新人參與開(kāi)發(fā)時(shí),需要很高的學(xué)習(xí)和溝通成本,還經(jīng)常出現(xiàn)新人提交代碼時(shí)由于沒(méi)有遵守代碼規(guī)范反復(fù)被要求修改。而使用Lint后,這些問(wèn)題都能在第一時(shí)間得到解決,節(jié)省了大量的人力,提高了代碼質(zhì)量和開(kāi)發(fā)效率,也提高了App的使用體驗(yàn)。
參考資料與擴(kuò)展閱讀
- 使用 Lint 改進(jìn)您的代碼 | Android Studio
- Android Plugin DSL Reference:LintOptions
- Android自定義Lint實(shí)踐
- Lint工具的源碼分析(3)
- Android Studio Release Notes
- Git - Documentation
Lint和Gradle相關(guān)技術(shù)細(xì)節(jié)還可以閱讀個(gè)人博客:
- Android Lint:基本使用與配置
- Android Lint:自定義Lint調(diào)試與開(kāi)發(fā)
- Android Gradle配置快速入門
- Gradle開(kāi)發(fā)快速入門——DSL語(yǔ)法原理與常用API介紹
作者簡(jiǎn)介
- 子健,Android高級(jí)工程師,2015年畢業(yè)于西安電子科技大學(xué)并校招加入美團(tuán)外賣。前期先后負(fù)責(zé)過(guò)外賣App首頁(yè)、商家容器、評(píng)價(jià)等核心業(yè)務(wù)模塊的開(kāi)發(fā)維護(hù),目前重點(diǎn)負(fù)責(zé)參與外賣打包自動(dòng)化、代碼檢查、平臺(tái)化等技術(shù)工作。
招聘
美團(tuán)外賣App團(tuán)隊(duì)誠(chéng)招Android/iOS高級(jí)工程師/技術(shù)專家,工作地北京/上海可選,歡迎有興趣的同學(xué)投遞簡(jiǎn)歷到wukai05#meituan.com。
總結(jié)
以上是生活随笔為你收集整理的美团外卖Android Lint代码检查实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 美团数据库运维自动化系统构建之路
- 下一篇: 牛逼!支付宝高级Java三面题目:线程锁
