
如果开发人员不够谨慎,事情可能会变得很糟糕。 例如,经典的开发人员遗漏是使用与旧代码不兼容的API新版本,执行需要特殊用户权限的操作,以及应用程序本地化方面的空白。 这些只是其中的一些。
此外,Java和Kotlin与其他任何编程语言一样,都有自己的构造,这些构造可能导致性能下降。
你好皮棉
我们使用称为Lint(或Linter)的工具来避免此类问题。 Lint是静态代码分析工具,可帮助开发人员在代码编译之前发现潜在问题。 Lint对源代码执行多次检查,可以检查诸如未使用的变量或函数参数,条件简化,范围错误,变量或函数未定义,代码优化不佳等问题。 当我们谈论Android开发时,有数百个Lint可用 。
但是有时我们需要在代码中找到这些现有检查未涵盖的特定问题。
您好自定义检查Lint
在开始编码之前,让我们定义目标,并了解如何使用Lint API逐步实现它。 目的是创建检查以检测对象的错误方法调用。 该测试的目的是确定在View组件上安装侦听器的方法是否会中断对该组件的几次连续单击,以便我们避免多次打开同一Activity或访问网络。
Lint用户检查是作为标准Java(或Kotlin)模块的一部分编写的。 最简单的入门方法是创建一个简单的基于Gradle的项目(不一定是Android项目)。
然后添加Lint依赖项。 在模块的build.gradle
文件中添加:
compileOnly "com.android.tools.lint:lint-api:$lintVersion" compileOnly "com.android.tools.lint:lint-checks:$lintVersion"
现在,在研究此主题时,我学到了一个技巧。 lintVersion
应该是gradlePluginVersion + 23.0.0
。 gradlePluginVersion
是在项目级别的build.gradle
文件中定义的变量。 目前,最新的稳定版本是3.3.0。 这意味着lintVersion
应该为26.3.0。
每张Lint支票包含4个部分:
- 问题是我们试图防止的代码问题。 当Lint检查失败时,这将报告给开发人员。
- 检测器是提供Lint API的问题发现工具。
- 实现是可能出现问题的区域(源文件,XML文件,编译的代码等)。
- 注册表是自定义的Lint检查注册表,它将与包含预定义检查的现有注册表一起使用。
实作
让我们开始为自定义验证创建一个实现。 每个实现都包含一个实现检测器和范围的类。
val correctClickListenerImplementation = Implementation(CorrectClickListenerDetector::class.java, Scope.JAVA_FILE_SCOPE)
请记住, Scope.JAVA_FILE_SCOPE
也适用于Kotlin类。
问题
下一步是使用此实现来确定问题。 每个问题都包含以下几个部分:
- ID是唯一标识符。
- 说明 -问题的简短说明 (5-6个字)。
- 说明对问题的完整说明,并提供有关解决方法的建议。
- 类别 -问题的类别(性能,翻译,安全性等)。
- 优先级 -问题的重要性,范围为1到10,其中10为最高。 这将用于在启动Lint时创建的报告中对问题进行排序。
- 严重性-问题的严重性(严重,错误,警告,信息或忽略)。
- 一个实现是将用于检测此问题的实现。
val ISSUE_CLICK_LISTENER = Issue.create( id = "UnsafeClickListener", briefDescription = "Unsafe click listener", explanation = """" This check ensures you call click listener that is throttled instead of a normal one which does not prevent double clicks. """.trimIndent(), category = Category.CORRECTNESS, priority = 6, severity = Severity.WARNING, implementation = correctClickListenerImplementation )
检测器
Lint API为您可以在实现中定义的每个区域提供接口。 这些接口中的每一个都提供了一些方法,您可以覆盖和访问您感兴趣的代码部分。
- UastScanner -Java或Kotlin文件(UAST- 统一抽象语法树 (俄语统一抽象语法树 ))。
- ClassScanner-编译文件(字节码)。
- BinaryResourceScanner-二进制资源,例如位图或
res/raw
文件。 - ResourceFolderScanner-资源文件夹(不是其中的特定文件)。
- XmlScanner -XML文件。
- GradleScanner -Gradle文件。
- OtherFileScanner-其他所有内容。
另外, Detector
类是一个基类,具有上述每个接口提供的所有方法的空实现,因此,如果只需要一个方法,则无需实现完整接口。
现在,我们准备实现一个检测器,它将检查对象的正确方法调用。
private const val REPORT_MESSAGE = "Use setThrottlingClickListener" class CorrectClickListenerDetector : Detector(), Detector.UastScanner { override fun getApplicableUastTypes(): List<Class<out UElement>>? { return listOf<Class<out UElement>>(UCallExpression::class.java) } /** * UAST, , * UAST . UElementHandler, * , , * , , , , .. * , * . — , * , , . */ override fun createUastHandler(context: JavaContext): UElementHandler? { return object: UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if (node.methodName != null && node.methodName?.equals("setOnClickListener", ignoreCase = true) == true) { context.report(ISSUE_CLICK_LISTENER, node, context.getLocation(node), REPORT_MESSAGE, createFix()) } } } } private fun createFix(): LintFix { return fix().replace().text("setOnClickListener").with("setThrottlingClickListener").build() } }
注册表
我们需要做的最后一件事是在注册表中添加问题,并告诉Lint它应该与标准注册表一起使用特殊的问题注册表。
class MyIssueRegistry : IssueRegistry() { override val issues: List<Issue> = listOf(ISSUE_CLICK_LISTENER) }
在build.gradle
模块级别:
jar { manifest { attributes("Lint-Registry-v2": "co.infinum.lint.MyIssueRegistry") } }
其中co.infinum.lint
是MyIssueRegistry
类的包。 现在,您可以使用gradlew
脚本运行jar
任务,该库应出现在build/libs
目录中。
这是Lint用户检查的另一个示例,您可以在其中查看如何处理XML文件。
使用方法
新的Lint支票已准备就绪,可以在项目中使用。 如果此检查可以应用于所有项目,则可以将其放在~/.android/lint
文件夹中(如果尚不存在,则可以创建它)。
另外,您可以将检查放在项目中的单独模块中,并使用lintChecks
方法像其他任何依赖项一样启用此模块。
值得吗?
Lint是每个开发人员都应该使用的非常好的工具。 能够及早发现代码中的潜在问题非常有帮助。 尽管自定义检查不容易编写,主要是由于API的复杂性,但绝对值得,并且可以在将来节省大量时间和精力。