Writing custom lint in Android: The ultimate teacher

Alok Bharti
4 min readSep 28, 2020

--

Remember, when your teacher always interrupted you to avoid doing something wrong. We always had two choices, either to ignore it (not realizing its importance) or to consider it for our better future.

Lint is quite similar to our teachers, it’ll alert you while you’re writing your code and let you know how you can fix them. Isn’t it amazing?

Android Studio highlights that there is a typo in my code and maybe we should use something more meaningful.

Let me tell you my use case and why I decided to write my own custom link check. In one of my projects, we’re displaying a dialog that shows a successful API call after the user submits their data and there is also a button on it to close the dialog. And to do that we’ve to implement an interface that contains the method onClose() .

Something like this:

So while calling the dialog class, I always forgot to implement the listener, and due to that my app crashed (it happened thrice😔).

Okay, enough chit chat. Let’s talk about the real stuff: how to write custom lint checks?

First, change your project view from android to project. Create a new module and then select the ‘Java or Kotlin library’ template. Let’s name it ‘rules’.

Now, open build.gradle file of rules module and add the following dependencies for lint (just FYI, lintVersion = gradlePluginVersion + 23.0.0 always):

And, in your app-level build.gradle file, add this:

Now, create a new kotlin file in the rules module called MyDetector where we write our code to detect whether the interface is implemented by a class that uses our SuccessDialogFragment or not.

There are many types of detectors. Some of them are:

  • XmlScanner — XML files (visit with DOM)
  • UastScanner — Java and Kotlin files (visit with UAST)
  • ClassScanner — .class files (bytecode, visit with ASM)
  • BinaryResourceScanner — binaries like images
  • ResourceFolderScanner — android res folders
  • GradleScanner — Gradle build scripts • OtherFileScanner — Other files

In our case, we’ll use UastScanner since we’ve to look for the classes that have declared an instance of SuccessDialogFragment class.

class MyDetector : Detector(), Detector.UastScanner{}

Declare an Implementation variable that basically defines the detector class and its scope (The scope of a detector is the set of files a detector must consider when performing its analysis).

Declare an Issue inside my detector class. An issue defines the metadata for our problems. It contains id, description, explanation, category, priority (from 1 to 10), and severity.

Now, comes the interesting part. Since we’re looking for the code that has the constructor of SuccessDialogFragment class, there is a method called getApplicationConstructorTypes() in which we can define what type of class we’re looking for. In my case it is com.example.SuccessDialogFragment

So, it’s like:

And it also provides another method called visitConstructor(). So if the detector has found the constructor that we define in constructor types, then it’ll go inside this method.

Here, JavaContext will give the context of the parent class/file in which we’re inspecting.

UCallExpression will give us the details about the method where the constructor is called. Basically, Everything start with ‘U’ (like UCallExpression, UExpression)will give you the information about the class/file you’re looking at.

And, PsiMethod will give us the details about the constructor itself.

Now what we’re doing here is, we’re looking for the entire source code and searching for our interface of the SuccessDialogFragment class. If it doesn’t, then we find the location of the declaration point and then report it there.

Here is the entire Detector class:

The last thing that we’ve do is create an IssueRegistry class that will let the lint infrastructure know about the issue we’ve.

And we’ll expose it by adding MyIssueRegistry class in the rules gradle file inside a manifest.

And finally, open your terminal and run ./gradlew lint

When I write this in any class, I get the above message as expected.

Well, this was really interesting and I guess you must have already realized its potential and how we can use it according to our requirements.

Here are the best resources that I found useful:

  1. KotlinConf 2017: Kotlin static analysis with Android Lint by Tor Norbye (you can also download the slides for this talk)
  2. Getting most out of Android Lint: Android Dev Summit’18 by Matthew Gharrity
  3. Article by Niklas Baudy

Hope you learned something in this article. Let’s connect on LinkedIn or Twitter and feel free to share your feedback and your thoughts.

Happy coding! Peace out✌

--

--