As of 3.0, you can use the ServiceLoader mechanism to register your lint check; you no longer need to insert a special manifest key entry into the lint.jar; instead you just register your IssueRegistry by listing its fully qualified name in src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry.
However, in the past, with the manifest approach to loading lint checks, I had a simple way of discarding older, potentially incompatible checks: Changing the key name. That doesn't work in the service loader world.
I've thought of various ways of fixing this, and I would really appreciate your suggestions on better way to fix this. But for now, for canary 4, I've added this mechanism:
IssueRegistry has a new "api" property; a simple int.
When you write your custom IssueRegistry implementations, you'll write them like this:
import com.android.tools.lint.detector.api.CURRENT_API
import com.android.tools.lint.detector.api.Issue
class MyIssueRegistry : IssueRegistry() {
override val issues: List<Issue> = listOf(MY_ISSUE)
override val api: Int = CURRENT_API
}
The new part is the "override val api" part. Basically, your registry is stating which API level it was compiled with. This is a constant, so the value (from the lint api jar) gets inlined into your issue registry at compilation time. When your custom check is loaded, lint will see if it matches the hosting lint environment, and if not, lint will warn that the lint check may not work correctly.
Lint then tries to instantiate the checks, and if that throws any exceptions, lint tells you that the checks definitely don't work and it will skip them completely. This will be the case for checks that use the older JavaScanner and JavaPsiScanner (e.g. Lombok and non-UAST PSI checks), since I've just removed all the support for that in 3.1; it removed a lot of deprecated code.
Lint may also encounter a check that reports a higher compilation API than the current lint check -- e.g. you may compile your custom lint checks against 3.1, but somebody on the team then runs the check on an older lint, let's say 3.0. In that case lint will again warn that the versions don't match. But, if you've tested your check and you've made sure it also works on 3.0, you call tell lint that by overriding a second attribute:
override val minApi: Int = 2
Here you're saying that lint also works with all versions from 2 and up the value listed for the api property. (The various version levels are found in the ApiKt class; there's a describeApi(level: Int) function you can call).
Anyway, this seemed like a pretty simple way to support this usecase. I considered trying to solve this a different way, e.g. updating the packaging steps for lint checks such that we instead insert metadata into your lint jar's META-INF folder recording the various API information. But lint jars are packaged in many different ways, including custom mechanisms and via other build systems, so that seemed like it could be brittle. This now makes it pretty explicit.
Thoughts, suggestions? I have a few days left before the next canary cut-off before this goes out so I can still make changes!
-- Tor
P.S. Here's how it would look from a Java implementation of an issue registry:
import com.android.tools.lint.detector.api.ApiKt;
import com.android.tools.lint.detector.api.Issue;
import java.util.Collections;
import java.util.List;
public class MyIssueRegistry extends IssueRegistry {
@Override
public List<Issue> getIssues() {
return Collections.singletonList(MY_ISSUE);
}
@Override
public int getApi() {
return ApiKt.CURRENT_API;
}
}
P.S.2: Here are some examples of the kinds of warning messages you now get for lint checks that don't supply api == CURRENT_API:
lint3.jar: Warning: Lint found an issue registry (com.example.google.lint.MyIssueRegistry) which did not specify the Lint API version it was compiled with.
This means that the lint checks are likely not compatible.
To fix this, make your lint IssueRegistry class contain
override val api: Int = com.android.tools.lint.detector.api.CURRENT_API
or from Java,
@Override public int getApi() { return com.android.tools.lint.detector.api.ApiKt.CURRENT_API; } [ObsoleteLintCustomCheck]
lint3.jar: Warning: Lint found one or more custom checks that could not be loaded. The most likely reason for this is that it is using an older, incompatible or unsupported API in lint. Make sure these lint checks are updated to the new APIs. The issue registry class is com.example.google.lint.MyIssueRegistry. The class loading issue is com/android/tools/lint/detector/api/Detector$JavaPsiScanner: ClassLoader.defineClass1(ClassLoader.java:-2)←ClassLoader.defineClass(ClassLoader.java:763)←ClassLoader.defineClass(ClassLoader.java:642)←UrlClassLoader._defineClass(UrlClassLoader.java:272)←UrlClassLoader.defineClass(UrlClassLoader.java:268)←UrlClassLoader.findClass(UrlClassLoader.java:222)←ClassLoader.loadClass(ClassLoader.java:424)←ClassLoader.loadClass(ClassLoader.java:357) [ObsoleteLintCustomCheck]
0 errors, 2 warnings