之前對下面的問題,個人態度是,不報錯就是沒問題,報錯就用快捷鍵,根據Android Studio提示修復問題,歷來不去問個爲何?如今代碼潔癖症愈來愈嚴重的我,忍不住想看清什麼東西在搞鬼。html
認真看完本文,必定能夠學到最新的知識。就算看不下去,也要點個贊收藏,絕對不虧。本文並非吐槽Lint的很差,而是在學習Lint過程碰到問題,心態是奔潰的,以及解決每一個問題帶來的喜感。java
不知道你們有沒有注意項目中黃色代碼塊的提示,以下圖所示: node
或者紅色標記的代碼(並無任何錯誤),以下圖所示: 上文黃色的提醒和紅色警告,都是來自Android Studio內置的Lint工具檢查咱們的代碼後而做出的動做。 經過配置Lint,也能夠消除上面的提醒。例如,我開發系統APK,根本不須要考慮用戶是否受權。 那麼Lint是什麼呢?Android Studio 提供一個名爲Lint的靜態代碼掃描工具,能夠發現並糾正代碼結構中的質量問題,而無需實際執行該應用,也沒必要編寫測試用例。 Lint 工具可檢查您的 Android 項目源文件是否包含潛在錯誤,以及在正確性、安全性、性能、易用性、便利性和國際化方面是否須要優化改進。android
也就是說,經過Lint工具,咱們能夠寫出更高質量的代碼和代碼潛在的問題,媽媽不再用擔憂個人同事用中文命名了。也能夠經過定製Lint相關配置,提升開發效率。git
因爲Android Studio內置了Lint工具,好像不須要咱們幹嗎。但是呀,我有強迫症,看着上面的黃色塊,超級不爽的。因此咱們得了解如何配置Lint,讓它爲咱們服務,而不是爲Google服務。github
本文開始的紅色錯誤能夠經過註解來消除(通常建議是根據提示進行修正,除非明白本身在作什麼),能夠在類或該代碼所在的方法添加@SuppressLint
。api
@SuppressLint(all)
。 對XMl文件的禁止,則能夠採用以下形式:
namespace xmlns:tools="http://schemas.android.com/tools"
複製代碼
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="UnusedResources" >
<TextView
android:text="@string/auto_update_prompt" />
</LinearLayout>
複製代碼
父容器聲明瞭ignore屬性,那麼子視圖會繼承該屬性。例如上文LinearLayout中聲明瞭禁止Lint檢查LinearLayout的UnusedResources問題,TextView天然也禁止檢查該問題。禁止檢查多個問題,問題之間用逗號隔開;禁止檢查全部問題則使用all
關鍵字。安全
tools:ignore="all"
複製代碼
咱們也能夠經過配置項目的Gradle文件來禁止檢查。bash
例如禁止Lint檢查項目AndroidManifest.xml文件的GoogleAppIndexingWarning問題。在項目對應組件工程的Gradle文件添加以下配置,這樣就不會有黃色提醒了。併發
defaultConfig{
lintOptions {
disable 'GoogleAppIndexingWarning'
}
}
複製代碼
那麼,能夠禁止lint工具檢查什麼問題?
在上文中經過註解和在xml使用屬性來禁止Lint工具檢查相關問題,其實已是對Lint的配置了。Lint將多個問題歸爲一個issue(規則),例以下圖右邊的的六大規則。
上圖是Lint工具的工做流程,下面瞭解相關概念。 App Source Files 源文件包含組成 Android 項目的文件,包括 Java 和 XML 文件、圖標和 ProGuard 配置文件等。 lint.xml 文件 此配置文件可用於指定您但願排除的任何 Lint 檢查以及自定義問題嚴重級別。 lint Tool 咱們能夠經過Android Studio 對 Android 項目運行此靜態代碼掃描工具。也能夠手動運行。Lint 工具檢查可能影響 Android 應用質量和性能的代碼結構問題。 Lint 檢查結果 咱們能夠在控制檯(命令行運行)或 Android Studio 的 Inspection Results 窗口中查看 Lint 檢查結果。經過Lint工具的工做流程瞭解到,能夠在lint.xml文件配置一些信息。通常新建項目都是沒有lint.xml文件的,在項目的根目錄建立lint.xml文件。格式以下:
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- list of issues to configure -->
</lint>
複製代碼
那麼有哪些Issues(規則)呢?
在Android主要有以下六大類:
其餘更多Issues,能夠通將命令行切換到../Android/sdk/tools/bin目錄下,而後輸入lint --list
。例如在Mac下: cd /Users/gitcode8/Library/Android/sdk/tools/bin
輸入./lint --list
結果以下:
例如官網提供的參考例子:
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- 忽略整個工程目錄下指定問題的檢查 -->
<issue id="IconMissingDensityFolder" severity="ignore" />
<!-- 忽略對指定文件指定問題的檢查 -->
<issue id="ObsoleteLayoutParam">
<ignore path="res/layout/activation.xml" />
<ignore path="res/layout-xlarge/activation.xml" />
</issue>
<!-- 更改檢查問題歸屬的嚴重性 -->
<issue id="HardcodedText" severity="error" />
</lint>
複製代碼
學習Lint工具僅僅是爲了安撫個人強迫症?不,還不知道Lint真正用來幹嗎呢?
很差容易開發了個APP,準備開始上班摸魚了。還讓代碼自查?那就經過Lint來看看代碼質量如何吧。
爲何要自定義呢?已有規則不符合本身或團隊開發需求,或者以爲Lint存在一些缺陷。在網上大多數文章千篇一概,都是經過將Log打印來舉例,看着都好累哦。因爲沒有相關官方文檔和第三方教程(可能因爲lint的api更新太快,沒人願意作這種吃力不討好的工做),也這就只有這樣了。本文經過自定義命名規範規則來說解整個過程。
先學習相關api,能夠快速理解一些概念,能夠粗略看過,下結實踐再回來看。
Issue如上文所說,表示lint 工具檢查的一個規則,一個規則包含若干問題。常在Detector中建立。下文是建立一個Issue的例子。
private static final Issue ISSUE = Issue.create("NamingConventionWarning",
"命名規範錯誤",
"使用駝峯命名法,方法命名開頭小寫,類大寫字母開頭",
Category.USABILITY,
5,
Severity.WARNING,
new Implementation(NamingConventionDetecor.class,
EnumSet.of(Scope.JAVA_FILE)));
複製代碼
這樣就能很清楚的定義一個規則,上文只定義了檢查命名規範的規則。
用於註冊要檢查的Issue(規則),只有註冊了Issue,該Issue才能被使用。例如註冊上文的命名規範規則。
public class Register extends IssueRegistry {
@NotNull
@Override
public List<Issue> getIssues() {
return Arrays.asList(NamingConventionDetector.ISSUE);
}
}
複製代碼
查找指定的Issue,一個Issue對應一個Detector。自定義Lint 規則的過程也就是重寫Detector類相關方法的過程。具體看下小結實踐。
掃描並發現代碼中的Issue,Detector須要實現Scaner,能夠繼承一個到多個。
舊版本的JavaScanner、JavaPsiScanner隨着版本的更新已經被UastScanner替代了。
經過實現命名規範Issue來熟悉和運用上小節相關的api。自定義規則須要在Java工程中建立,這裏經過Android Studio來建立一個Java Library。
步驟:File->New->New Mudle->Java Library
這裏Library Name爲lib。
定義類NamingConventionDetector,並繼承自Detector。由於這裏是檢測Java文件類名和方法是否符合規則,因此實現Detector.UastScanner接口。
public class NamingConventionDetector
extends Detector
implements Detector.UastScanner {
}
複製代碼
在NamingConventionDetector類內定義上文的Issue:
public class NamingConventionDetector
extends Detector
implements Detector.UastScanner {
public static final Issue ISSUE = Issue.create("NamingConventionWarning",
"命名規範錯誤",
"使用駝峯命名法,方法命名開頭小寫",
Category.USABILITY,
5,
Severity.WARNING,
new Implementation(NamingConventionDetector.class,
EnumSet.of(Scope.JAVA_FILE)));
}
複製代碼
重寫Detector的createUastHandler方法,實現咱們本身的處理類。
public class NamingConventionDetector extends Detector implements Detector.UastScanner {
//定義命名規範規則
public static final Issue ISSUE = Issue.create("NamingConventionWarning",
"命名規範錯誤",
"使用駝峯命名法,方法命名開頭小寫",
Category.USABILITY,
5,
Severity.WARNING,
new Implementation(NamingConventionDetector.class,
EnumSet.of(Scope.JAVA_FILE)));
//返回咱們全部感興趣的類,即返回的類都被會檢查
@Nullable
@Override
public List<Class<? extends UElement>> getApplicableUastTypes() {
return Collections.<Class<? extends UElement>>singletonList(UClass.class);
}
//重寫該方法,建立本身的處理器
@Nullable
@Override
public UElementHandler createUastHandler(@NotNull final JavaContext context) {
return new UElementHandler() {
@Override
public void visitClass(@NotNull UClass node) {
node.accept(new NamingConventionVisitor(context, node));
}
};
}
//定義一個繼承自AbstractUastVisitor的訪問器,用來處理感興趣的問題
public static class NamingConventionVisitor extends AbstractUastVisitor {
JavaContext context;
UClass uClass;
public NamingConventionVisitor(JavaContext context, UClass uClass) {
this.context = context;
this.uClass = uClass;
}
@Override
public boolean visitClass(@org.jetbrains.annotations.NotNull UClass node) {
//獲取當前類名
char beginChar = node.getName().charAt(0);
int code = beginChar;
//若是類名不是大寫字母,則觸碰Issue,lint工具提示問題
if (97 < code && code < 122) {
context.report(ISSUE,context.getNameLocation(node),
"the name of class must start with uppercase:" + node.getName());
//返回true表示觸碰規則,lint提示該問題;false則不觸碰
return true;
}
return super.visitClass(node);
}
@Override
public boolean visitMethod(@NotNull UMethod node) {
//當前方法不是構造方法
if (!node.isConstructor()) {
char beginChar = node.getName().charAt(0);
int code = beginChar;
//當前方法首字母是大寫字母,則報Issue
if (65 < code && code < 90) {
context.report(ISSUE, context.getLocation(node),
"the method must start with lowercase:" + node.getName());
//返回true表示觸碰規則,lint提示該問題;false則不觸碰
return true;
}
}
return super.visitMethod(node);
}
}
}
複製代碼
上文NamingConventionDetector類,已是所有代碼,只檢查類名和方法名是否符合駝峯命名法,能夠根據具體需求,重寫抽象類AbstractUastVisitor的visitXXX方法。
若是處理特定的方法或者其餘,也可使用默認的處理器。重寫Scanner相關方法。例如:
@Override
public List<String> getApplicableMethodNames() {
return Arrays.asList("e","v");
}
複製代碼
表示e(),v()方法會被檢測到,並調用visitMethod()方法,實現本身的邏輯。
@Override
public void visitMethod JavaContext context, JavaElementVisitor visitor, PsiMethodCallExpression call, PsiMethod method) {
//todo something
super.visitMethod(context, visitor, call, method);
}
複製代碼
接下來就是註冊自定義的Issue:
public class Register extends IssueRegistry {
@NotNull
@Override
public List<Issue> getIssues() {
return Arrays.asList(NamingConventionDetector.ISSUE);
}
}
複製代碼
在lib項目的build.gradle文件添加相關代碼:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.tools.lint:lint-api:26.4.2'
implementation 'com.android.tools.lint:lint-checks:26.4.2'
}
//添加以下代碼
jar {
manifest {
attributes 'Lint-Registry': 'com.gitcode.lib.Register'
}
}
sourceCompatibility = "7"
targetCompatibility = "7"
複製代碼
到這裏就自定義Lint自定義規則就搞定了,接着是使用和肯定規則是否正確。
使用自定義Lint規則有兩種形式:jar包和AAR文件。
在Android Studio的Terminal輸入下面命令:
./gradlew lib:assemble
複製代碼
看到BUILD SUCCESSFUL
則表示生成jar包成功,能夠在下面路徑找到:
lib->build->libs
複製代碼
如圖:
將lib.jar拷貝下面目錄:~/.android/lint/
複製代碼
若是lint文件夾不存在,則建立。經過命令行輸入lint --list。滑到最後能夠看到配置的規則,如圖:
重啓Android Studio,讓規則生效。 檢測到方法大寫,不符合命名規範,報導該問題。 類名不符合規範: 從上文能夠看到,放在目錄下的jar包對全部工程都是有效的。若是要針對單個工程,那麼就須要須要AAR形式了。在同個工程新建一個Android Library,名爲lintLibrary,修改相關配置。
修改自定義lint規則的Java庫的build.gradle(這裏是上文的Java lib庫),注意到要將implementation改成compileOnly。
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//將implementation改成compileOnly,否則報錯
compileOnly 'com.android.tools.lint:lint-api:26.4.2'
compileOnly 'com.android.tools.lint:lint-checks:26.4.2'
}
jar {
manifest {
attributes 'Lint-Registry-v2': 'com.gitcode.lib.Register'
}
}
sourceCompatibility = "7"
targetCompatibility = "7"
複製代碼
Android Library主要用來輸出AAR文件,要注意到Android Studio新特性的變動(在這裏踩了大坑)。
dependencies {
......
lintPublish project(':lib')
}
複製代碼
在Android Studio 3.4+,lintChecks project(':lib')
:lint檢查只在當前工程生效,也就是Android Library,並不會打包到AAR文件中。lintPublish project(':lib')
纔會將lint檢查包含AAR文件中。
此時跟輸出普通的AAR文件沒什麼區別,但爲了手把手教會第一個自定義Issue,我寫!
步驟:
菜單欄:View->Tool Windows->Gradle
複製代碼
此時Android Studio在右邊會打開以下窗口:
根據上圖操做,雙擊assemble,稍等一會,在控制檯看BUILD SUCCESSFUL
,則可在下面目錄找到AAR文件。
lintLibrary->build->outputs->aar
複製代碼
這一小節的步驟也能夠經過命令行執行。
有本地依賴或者上傳遠程倉庫,這裏只介紹本地依賴。將上小結生成的AAR文件拷貝在app的libs文件夾。並配置app組件的build.gradle
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
implementation (name:'lintlibrary-release', ext:'aar')
}
複製代碼
到這裏,就能使用自定義的lint規則了,效果和上面使用jar包是一致的。若是不生效,重啓Android Studio看看。
這是由於在輸出AAR文件中,參考其餘人的文章。沒有將Java Library的依賴改成compileOnly
。並且Android Library中使用lintChecks
。
不知道爲何,Linkedin的參考文章沒有生效,多是Android Studio版本的問題。
另外使用lintChecks輸出AAR不生效,Android Studio 3.4+新特性變動,採用lintPublish(AGP 3.4+)。
花了好長好長的時間寫本文,差點就放棄了。由於本身Android Studio看不了lint的源碼,只能從網上找,網上又找不到最新的doc。過濾太多雷同文章,差點想哭,一些最新的文章也跟不上相關技術的更新。。。
可是一切都值得,由於能幫助到想學習Android Studio lint工具的同窗,一塊兒嚮往美好的生活。
點個贊行不
寫此文找到的一些具備參考意義的文章:
另外:本文沒有demo,demo的代碼已經貼在文章裏了。