Skip to content

Commit b90b410

Browse files
committed
Add lint check that detects UI thread continuations.
1 parent 12a49fe commit b90b410

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

tools/lint/src/main/kotlin/CheckRegistry.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ class CheckRegistry : IssueRegistry() {
3131
NonAndroidxNullabilityDetector.NON_ANDROIDX_NULLABILITY,
3232
DeferredApiDetector.INVALID_DEFERRED_API_USE,
3333
ProviderAssignmentDetector.INVALID_PROVIDER_ASSIGNMENT,
34-
ThreadPoolDetector.THREAD_POOL_CREATION
34+
ThreadPoolDetector.THREAD_POOL_CREATION,
35+
TasksMainThreadDetector.TASK_MAIN_THREAD,
3536
)
3637

3738
override val api: Int
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.google.firebase.lint.checks
2+
3+
import com.android.tools.lint.detector.api.Category
4+
import com.android.tools.lint.detector.api.Detector
5+
import com.android.tools.lint.detector.api.Implementation
6+
import com.android.tools.lint.detector.api.Issue
7+
import com.android.tools.lint.detector.api.JavaContext
8+
import com.android.tools.lint.detector.api.Scope
9+
import com.android.tools.lint.detector.api.Severity
10+
import com.android.tools.lint.detector.api.SourceCodeScanner
11+
import com.intellij.psi.PsiClass
12+
import com.intellij.psi.PsiMethod
13+
import com.intellij.psi.PsiParameter
14+
import org.jetbrains.uast.UCallExpression
15+
16+
@Suppress("DetectorIsMissingAnnotations")
17+
class TasksMainThreadDetector : Detector(), SourceCodeScanner {
18+
19+
override fun getApplicableMethodNames(): List<String> =
20+
listOf(
21+
"addOnSuccessListener",
22+
"addOnFailureListener",
23+
"addOnCompleteListener",
24+
"addOnCanceledListener",
25+
"continueWith",
26+
"continueWithTask",
27+
"onSuccessTask"
28+
)
29+
30+
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
31+
if (!isTaskMethod(method)) {
32+
return
33+
}
34+
35+
val firstArgument: PsiParameter = method.parameterList.parameters.firstOrNull() ?: return
36+
if (!firstArgument.type.equalsToText("java.util.concurrent.Executor")) {
37+
context.report(
38+
TASK_MAIN_THREAD,
39+
context.getCallLocation(node, includeReceiver = false, includeArguments = false),
40+
"Use an Executor explicitly to avoid running on the main thread."
41+
)
42+
}
43+
}
44+
45+
private fun isTaskMethod(method: PsiMethod): Boolean {
46+
(method.parent as? PsiClass)?.let {
47+
return it.qualifiedName == "com.google.android.gms.tasks.Task"
48+
}
49+
return false
50+
}
51+
52+
companion object {
53+
private val IMPLEMENTATION =
54+
Implementation(TasksMainThreadDetector::class.java, Scope.JAVA_FILE_SCOPE)
55+
56+
/** Calling methods on the wrong thread */
57+
@JvmField
58+
val TASK_MAIN_THREAD =
59+
Issue.create(
60+
id = "TaskMainThread",
61+
briefDescription = "Use an explicit Executor for Task continuations",
62+
explanation =
63+
"""
64+
Not providing an executor results in continuations to execute on the main thread \
65+
which is not a good default. Please pass in an executor explicitly.
66+
""",
67+
category = Category.PERFORMANCE,
68+
priority = 6,
69+
severity = Severity.ERROR,
70+
implementation = IMPLEMENTATION
71+
)
72+
}
73+
}

0 commit comments

Comments
 (0)