共计 9460 个字符,预计需要花费 24 分钟才能阅读完成。
一、概述
Android Studio 提供了一个名为 Lint 的代码扫描工具,可帮忙开发者发现并更正代码构造品质方面的问题,并且无需您理论执行利用,也不用编写测试用例。零碎会报告该工具检测到的每个问题并提供问题的形容音讯和重大级别,以便开发者能够疾速确定须要优先进行的要害改良。此外,咱们还能够升高问题的重大级别以疏忽与我的项目无关的问题,或者进步重大级别以突出特定问题。
作为一款代码查看工具,Lint 工具能够查看 Android 我的项目源文件是否有潜在的 bug,以及在正确性、安全性、性能、易用性、无障碍性和国际化方面是否须要优化改良。应用 Android Studio 时,无论何时构建利用,都会运行配置的 lint 和 IDE 查看。并且,您能够手动运行查看或从命令行运行 Lint。
上面是 Lint 工具执行代码扫码的工作示意图:
Lint 工具有几个根底的概念须要帮大家理分明:
- 利用的源文件:源文件包含组成 Android 我的项目的各种文件,例如 Java、Kotlin 和 XML 文件,以及图标和 ProGuard 配置文件。
- lint.xml 文 件:一个配置文件,可用于指定要排除的任何 lint 查看以及自定义问题重大级别。
- lint 工具:一个动态代码扫描工具,能够从命令行或在 Android Studio 中对 Android 我的项目运行该工具来进行查看。lint 工具用于查看代码是否存在可能影响 Android 利用的品质和性能的构造问题。
- lint 查看后果:Lint 查看的后果,能够在控制台或 Android Studio 的 Inspection Results 窗口中查看 lint 查看后果。
二、Lint 对于团队开发的重要性
Android 官网对 Lint 性能的形容十分精确:通过进行 lint 查看来改良代码。这里关键词是改良,也就是当开发者写了一行语法正确并且运行失常的代码,然而它可能不是最优或最平安的写法,或最优的 API 组合,通过 Lint 检测将发现这些坏代码,通过实时提醒来辅助开发者改良为更强壮更平安的写法。上面是一些具体的应用场景阐明:
2.1 案例 1: 检测代码标准
在接入 ViewBinding 时,为了保障 xml 内的 view Id 与 ViewBinding 放弃一致性的驼峰命名,须要防止应用 btn_xx 下划线命名,这样不便浏览代码与检索代码。通常像这样的标准会在团队内集中同步,如何能保障在这个标准自同步后新提交的代码 viewId 100% 是驼峰命名呢?留神这里说的是达到 100% 符合规范,要彻底杜绝因人为导致的疏漏。
在实践中从两个点下手就能 100% 达成这一目标,第一是本地借助 IDE 实时检测 xml 文件,辅助开发者疾速改良代码,像上面这样:
2.2 案例 2:屏蔽不平安的原生 API
在应用 Integer.parseInt 等办法时有可能会触发 NumberFormatException 导致 Crash。有两种形式来躲避,一是应用对立封装的工具类,二是必须包一个 try catch。下图是实现这个检测要求的 Lint 成果。
2.3 案例 3:检测导致 Crash 的危险代码
在 App 侧肯定会用到序列化用于解析接口数据,实现序列化会带来解体危险,如果一个实现了序列化接口的类,而它的成员变量利用的类未实现序列化接口,就有可能引发解体。这种问题靠 code review 来杜绝难度是蛮大的,通常也心愿线上 code review 时更多关注业务逻辑而非低级的编码谬误,这样能无效缩小 code review 耗时。
针对这类问题须要编写一个 Lint 规定,检测实现了序列化接口的类其成员变量的类型是否实现了序列化接口,比方上面这样:
下面三个例子别离从团队标准与危险代码治理论述了 Lint 的作用,也体现了进去一点,Lint 天生是依赖团队来放大价值的,行将团队的积淀的技术教训,通过 Lint 疾速触达到每一个成员与每一个我的项目中,依此来一直拉高团队代码品质的底线,并长期保障不劣化。
三、根本应用
3.1 命令行运行 lint
咱们能够应用 Android Studio 来运行 Gradle 命令,应用 Gradle 封装容器对我的项目调用 lint 工作,例如:
//Windows
gradlew lint
// Linux 或 Mac
./gradlew lint
运行完之后,你将失去一份如下的报告:
> Task :app:lintDebug
Wrote HTML report to file:<path-to-project>/app/build/reports/lint-results-debug.html
当然,咱们还能够针对 Debug 和 Release 版本进行查看,对应的命令如下:
./gradlew lintRelease
不过,运行下面命令的前提是你曾经配置好了 lint 的查看规定。
3.2 lint 配置
默认状况下,当咱们运行 lint 扫描时,但凡 lint 可帮忙查看的问题,lint 工具都会查看是否存在。不过,咱们能够限定让 lint 只查看是否存在某些问题,并为这些问题指定重大级别。例如,咱们能够禁止 lint 查看是否存在与我的项目无关的特定问题,还能够将 lint 配置为以较低的重大级别报告非关键问题。反对的 Lint 查看的级别如下:
- 全局(整个我的项目)
- 我的项目模块
- 生产模块
- 测试模块
- 关上的文件
- 类层次结构
- 版本控制系统 (VCS) 范畴
3.2.1 在 Android Studio 中配置 lint
咱们能够应用 Android Studio 内置的 lint 工具来查看代码,并且能够通过以下两种形式查看正告和谬误:
- 在代码编辑器中查看以弹出的文本模式显示的正告和谬误。lint 发现问题后,会用黄色突出显示有问题的代码,而对于更重大的问题,它会在代码上面增加红色下划线。
- 顺次点击 Analyze > Inspect Code 后,在 lint Inspection Results 窗口中查看。
3.2.2 lint 文件配置
咱们能够在 lint.xml 文件中指定 lint 查看偏好设置,如果是手动创立此文件,能够将其搁置在 Android 我的项目的根目录下。lint.xml 文件由关闭的 <lint> 父标记组成,此标记蕴含一个或多个 <issue> 子元素,如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- list of issues to configure -->
</lint>
当然,咱们还能够通过在 <issue> 标记中设置重大级别属性来更改某个问题的重大级别或禁止对该问题进行 lint 查看。上面是一个示例:
<?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="UselessLeaf">
<ignore path="res/layout/main.xml" />
</issue>
<issue id="HardcodedText" severity="error" />
</lint>
3.2.3 配置 Java、Kotlin 和 XML 源文件的 lint 查看
有时候,咱们须要专门对 Android 我的项目中的某个类或办法停用 lint 查看,此时只须要在代码块中增加 @SuppressLint 注解即可。以下示例展现了如何对 onCreate 办法中的 NewApi 问题停用 lint 查看。
@SuppressLint("NewApi")
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
setContentView(R.layout.main)
要禁止 lint 查看文件中的所有问题,请应用 all 关键字,比方:
@SuppressLint("all")
而对于 XML 文件来说,能够应用 tools:ignore 属性对 XML 文件的特定局部停用 lint 查看,比方,上面的示例中对 <TextView> 子元素停用 lint 查看:
<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>
同样,要禁止 lint 查看 XML 元素中的所有问题,请应用 all 关键字。
tools:ignore="all"
3.3 通过 Gradle 配置 lint 选项
通过 Android Plugin for Gradle 工具,开发者能够应用模块级 build.gradle 文件中的 lint{} 代码块配置某些 lint 选项,例如要运行或疏忽哪些查看。
android {
...
lintOptions {
// Turns off checks for the issue IDs you specify.
disable("TypographyFractions")
disable("TypographyQuotes")
// Turns on checks for the issue IDs you specify. These checks are in
// addition to the default lint checks.
enable("RtlHardcoded")
enable("RtlCompat")
enable("RtlEnabled")
// To enable checks for only a subset of issue IDs and ignore all others,
// list the issue IDs with the 'check' property instead. This property overrides
// any issue IDs you enable or disable using the properties above.
checkOnly("NewApi", "InlinedApi")
// If set to true, turns off analysis progress reporting by lint.
quiet = true
// If set to true (default), stops the build if errors are found.
abortOnError = false
// If true, only report errors.
ignoreWarnings = true
// If true, lint also checks all dependencies as part of its analysis. Recommended for
// projects consisting of an app with library dependencies.
isCheckDependencies = true
}
}
...
3.4 示例利用
应用 Lint 前,在相应 module 的 build.gradle 文件的 android 配置项中,通过 lintOptions 来配置相应的属性,比方:
android {
lintOptions {
// 设置为 true 后,release 构建都会以 Fatal 的设置来运行 Lint。// 如果构建时发现了致命(Fatal)的问题,会停止构建(具体由 abortOnError 管制)checkReleaseBuilds true
// 设置为 true,则当 Lint 发现错误时进行 Gradle 构建
abortOnError false
// 从新指定 Lint 规定配置文件
lintConfig file("default-lint.xml")
// 仅查看指定的问题(依据规定的 id 指定)check 'NewApi', 'InlinedApi'
// 设置为 true 则查看所有的问题,包含默认不查看问题
checkAllWarnings true
// 不查看指定的问题(依据规定的 id 指定)disable 'TypographyFractions','TypographyQuotes'
// 查看指定的问题(依据规定的 id 指定)enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// 在报告中是否返回对应的 Lint 阐明
explainIssues true
// 设置为 true 则错误报告中不包含源代码的行号
noLines true
// 设置为 true 时 Lint 将不报告剖析的进度
quiet true
// 设置为 true 则显示一个问题所在的所有中央,而不会截短列表
showAll true
// 设置为 true 则只报告谬误
ignoreWarnings true
// 设置为 true,则当有谬误时会显示文件的全门路或绝对路径 (默认状况下为 true)
absolutePaths true
// 写入报告的门路,默认为构建目录下的 lint-results.html
htmlOutput file("lint-report.html")
// 设置为 true 则会生成一个 HTML 格局的报告
htmlReport true
// 写入检查报告的文件(不指定默认为 lint-results.xml)xmlOutput file("lint-report.xml")
// 设置为 true 则会生成一个 XML 报告
xmlReport false
// 配置写入输入后果的地位,格局能够是文件或 stdout
textOutput 'stdout'
// 设置为 true,则生成纯文本报告(默认为 false)textReport false
// 设置为 true,则会把所有正告视为错误处理
warningsAsErrors true
// 笼罩 Lint 规定的重大水平,例如:severityOverrides ["MissingTranslation": LintOptions.SEVERITY_WARNING]
// 将指定问题(依据 id 指定)的重大级别(severity)设置为 Fatal
fatal 'NewApi', 'InlineApi'
// 将指定问题(依据 id 指定)的重大级别(severity)设置为 Error
error 'Wakelock', 'TextViewEdits'
// 将指定问题(依据 id 指定)的重大级别(severity)设置为 Warning
warning 'ResourceAsColor'
// 将指定问题(依据 id 指定)的重大级别(severity)设置为 ignore
ignore 'TypographyQuotes'
}
}
而后,在我的项目根目录创立一个名为 default-lint.xml 的文件,在这个文件中能够本人定义规定的利用场景、报警级别、是否启用等。
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- list of issues to configure -->
<!-- Ignore the UselessLeaf issue in the specified file -->
<issue id="ApplySharedPref" severity="ignore"><!-- 对指定文件,疏忽该 issue-->
</issue>
<issue id="LogUsage" severity="error" />
</lint>
而后,应用后面介绍的 Gradle 命令行执行 Lint 查看即可。
./gradlew lint
./gradlew app:lintDebug
./gradlew app:lintTiktokI18nDebug
四、自定义 Lint
4.1 方案设计
自定义 Lint 规定最终都会打成 JAR 包,只需将该输入 JAR 提供给其余组件应用即可。目前有两种形式可供选择:全局计划和 aar 计划。
全局计划
把此 jar 拷贝到 ~/.android/lint/ 目录中即可。毛病不言而喻:针对所有工程失效,会影响同一台机器其余工程的 Lint 查看。即使触发工程时拷贝过来,执行完删除,但其余过程或线程应用 ./gradlew lint 仍可能会受到影响。
aar 计划
另一种实现形式是将 jar 置于一个 aar 中,如果某个工程想要接入执行自定义的 lint 规定,只需依赖这个公布后的 aar 即可,如此一来,新增的 lint 规定就可将影响范畴管制在单个我的项目内了,此计划也是 Google 目前举荐的形式。
参考文档:创立 Android 库
Android 的 Lint 有很多默认的规定,分为了不同的规定组和级别,从检测后果中咱们就可以看进去。比方规定层面有 Correctness(正确性),Performance(性能),Security(平安)等。当然,咱们也能够本人定义新的规定,比方不容许间接应用 android.util.Log 相干的办法,因为可能会有安全性问题。
4.2 自定义 Lint 示例
4.2.1 增加依赖
首先,咱们须要创立一个 java 的 module,用来实现自定义 lint 规定,须要引入 lint 相干的依赖。
implementation "com.android.tools.lint:lint-api:30.2.1"
implementation "com.android.tools.lint:lint-checks:30.2.1"
4.2.2 增加 Lint 规定
而后,咱们新建一个规定类,继承 Detector 并实现 Detector.UastScanner 接口,作用是实现代码检测的入口,代码如下。
public class LogDetector extends Detector implements Detector.UastScanner{
public static final Issue ISSUE = Issue.create(
"LogUse",
"防止间接应用 android.util.Log",
"防止间接应用 android.util.log, 请应用 xxx 代替",
Category.SECURITY,
5,
Severity.ERROR,
new Implementation(LogDetector.class, Scope.JAVA_FILE_SCOPE)
);
@Nullable
@Override
public List<String> getApplicableMethodNames() {return Arrays.asList("v", "d", "i", "w", "e");
}
@Override
public void visitMethod(@NotNull JavaContext context, @NotNull UCallExpression node, @NotNull PsiMethod method) {if (context.getEvaluator().isMemberInClass(method, "android.util.Log")) {context.report(ISSUE, node, context.getLocation(node), "请勿间接调用 Log,应该应用对立 Log 工具类");
}
}
}
在下面的代码中,咱们创立了一个 LogDetector 类,该类继承自 Detector,实现了 Scanner 的接口,这里的 UastScanner 是对 Java/kotlin 源文件进行扫描。而后,咱们重写了 getApplicableMethodNames 和 visitMethod。其中,getApplicableMethodNames 办法的作用是返回要检测的办法名;visitMethod 办法会在上述办法名的办法被调用时调用,所以在此办法内,咱们能够进行一些自定义的解决逻辑。最初,就是实现了一个规定的定义,检测到该问题时,通过 context.report()办法抛出。Issue 的参数阐明如下:
- id:惟一值,应该能简短形容以后问题。利用 Java 注解或者 XML 属性进行屏蔽时,应用的就是这个 id。
- summary:简短的总结,形容问题而不是修复措施。
- explanation:残缺的问题解释和修复倡议。
- category:问题类别。比方 Security、Performance、Usability、Accessibility 和 Correctness
- priority:优先级。1-10 的数字,10 为最重要 / 最重大。
- severity:重大级别:Fatal, Error, Warning, Informational, Ignore。
- Implementation:为 Issue 和 Detector 提供映射关系,Detector 就是以后 Detector。
同时,Scanner 反对的品种有以下几种:
- ClassScanner:Class 文件扫描器
- GradleScanner:扫描 Gradle 脚本
- ResourceFolderScanner:资源文件扫描器
- XmlScanner:扫描 XML 文件
- JavaScanner/JavaPsiScanner/UastSanner:扫描源文件
- OtherFileScanner:扫描其余类型文件
4.2.3 注册 Lint
当初,规定的实现和配置都已实现,要怎么注册到 Lint 之中呢?其实也很简略,咱们只须要继承 IssueRegistry 实现一个注册类,在 getIssues 办法中,注册咱们的规定配置即可,代码如下。
public class LogIssueRegistry extends IssueRegistry {
@Override
public List<Issue> getIssues() {
return Arrays.asList(LogDetector.ISSUE);
}
@Override
public int getMinApi() {return 1;}
@Override
public int getApi() {return ApiKt.CURRENT_API;}
}
4.2.4 生成 jar 包
接着,在 build.gradle 中申明 Lint-Registry,留神申明时包名要匹配,如下所示。
jar {
manifest {attributes("Lint-Registry": "com.xzh.lint.LogIssueRegistry")
}
}
4.2.5 引入 jar
对于 Demo 工程,咱们能够应用上面的形式引入:
lintChecks project(':lint-wrapper')
除此之外,咱们还能够应用 LinkedIn 计划进行引入,将 jar 包放到一个 aar 中,这样就能够通过引入 aar 的形式实现。
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
dependencies {lintPublish project(':lint-wrapper')
// other dependencies
}
当咱们运行我的项目的时候,在输入的 aar 包中,就能够看到 lint.jar。在指定模块中引入 aar,自定义的 Lint 就能够失效了。
最初,咱们在 app 模块中依赖一下 lint-aar 这个组件,就能够编写测试代码了。
参考:https://www.jianshu.com/p/0ae667d30e75