我希望确保在我的Android应用程序中(包括Java和Kotlin代码),不会调用特定类的特定方法。假设我有一个名为Bar
的类,其中包括两个方法:allowed()
和disallowed()
。以下是代码:
package com;
public class Bar {
public void disallowed() {
}
public void allowed() {
}
}
假设客户端代码可以调用allowed()
,但不应该调用disallowed()
。我找到了谷歌内置AddJavascriptInterfaceDetector的源代码,它与我的情况类似。
以下是我的lint规则代码:
SampleCodeDetector
package com.sample.mobile.lint
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
class SampleCodeDetector : Detector(), SourceCodeScanner {
companion object {
@JvmField
val ISSUE = Issue.create(
// ID: used in @SuppressLint warnings etc
"Usage of Bar#disallowed()",
// Title -- shown in the IDE's preference dialog, as category headers in the
// Analysis results window, etc
"Usage of Bar#disallowed() - Summary",
// Full explanation of the issue; you can use some markdown markup such as
// `monospace`, *italic*, and **bold**.
"This check highlights the usage of Bar#disallowed()",
Category.CORRECTNESS,
8,
Severity.ERROR,
Implementation(
SampleCodeDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
const val FULLY_QUALIFIED_CLASS_NAME = "com.Bar"
const val METHOD_NAME = "disallowed"
}
override fun getApplicableMethodNames() = listOf(METHOD_NAME)
override fun visitMethod(context: JavaContext, node: UCallExpression, method: PsiMethod) {
val evaluator = context.evaluator
if (!evaluator.methodMatches(method, FULLY_QUALIFIED_CLASS_NAME, true)) {
return
}
val message = "`Bar.disallowed()` should not be called"
context.report(ISSUE, node, context.getNameLocation(node), message)
}
}
样例问题注册表
package com.sample.mobile.lint
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.detector.api.Issue
class SampleIssueRegistry : IssueRegistry() {
override val issues: List<Issue> get() = listOf(SampleCodeDetector.ISSUE)
}
build.gradle
apply plugin: 'java-library'
dependencies {
String lintVersion = "26.1.1"
compileOnly "com.android.tools.lint:lint-api:$lintVersion"
compileOnly "com.android.tools.lint:lint-checks:$lintVersion"
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.60"
testImplementation "com.android.tools.lint:lint:$lintVersion"
testImplementation "com.android.tools.lint:lint-tests:$lintVersion"
}
sourceCompatibility = "1.8"
targetCompatibility = "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.sample.mobile.lint.SampleIssueRegistry")
}
}
SampleCodeDetectorTest
package com.sample.mobile.lint
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.detector.api.Detector
class SampleCodeDetectorTest : LintDetectorTest() {
private val javaFile = "package com.sample.mobile.app;\n" +
"\n" +
"import android.util.Log;\n" +
"\n" +
"import com.Bar;\n" +
"\n" +
"public class Foo {\n" +
"\n" +
" public void calLLog() {\n" +
" int a = 2;\n" +
" int b = 8;\n" +
" Log.d(\"Tag\", \"a+b=\" + (a + b));\n" +
" \n" +
" Bar bar = new Bar();\n" +
" bar.allowed();\n" +
" bar.disallowed();\n" +
" }\n" +
"}\n"
fun testJava() {
lint().files(LintDetectorTest.java(javaFile))
.run()
.expect("")
}
override fun getDetector(): Detector? {
return SampleCodeDetector()
}
override fun getIssues() = listOf(SampleCodeDetector.ISSUE)
}
很不幸,测试失败了:
org.junit.ComparisonFailure:
Expected :
Actual :No warnings.
当然,期望的结果不是空字符串,但无论如何,“No warnings.” 都是不正确的,因为在
class Foo
中调用了 Bar#disallowed()
。
addJavascriptInterface
的参数。disallowed()
没有任何参数。 - Maksim Dmitriev