Get started with Android Lint - Custom Lint Rules
Android build system provides a lot of tools to optimize the code, find potential issues, improve performance, etc. One of the tool it supports is Lint. Lint is a tool which analyzes the source code and flags potential bugs and errors during the build, and Android Studio even uses Lint to show those bugs and errors in the editor.
From wikipedia,
A linter or lint refers to tools that analyze source code to flag programming errors, bugs, stylistic errors, and suspicious constructs.
Android build system provides a lot of Lint rules that are quite helpful such as Hardcoded String
in xml layout, Missing Permissions
in Java/Kotlin code. After lint runs over the codebase, it generates a report in html and xml showing warnings and errors.
Sometimes, based on your requirements, you may need to write your custom lint rules. And that’s a good thing. Writing your custom Lint rules has never been easier! Android Studio 3 provides a great support to write and run your custom lint rules and you no longer have to copy jar file to ~/.android/lint
or some other place. It’s super easy now and you must use Lint!
Overview
Before writing some code, let’s understand different aspects of Lint. There are 4 main parts for every lint check.
- Issue: As the name suggests, this is what we are using lint for - to find issues in our code. Here, we define what issue we are looking for.
- Detector: Again, as the name suggests, a detector is used to detect issues in the source code. Here, we write the logic to find out the issues.
- Implementation: It binds an issue with a detector class and helps lint know where to look for a given issue.
- Registry: It’s a list of all the issues that lint should look for.
Example
Ideally, we should read more about each element, but what’s the fun in that? Let’s write a simple custom lint rule that runs on app’s xml layouts and registers a warning for every TextView
that does not use textAppearance
attribute.
Module
In your Android app project, create a new module and select Java Library
option. Once android studio creates the module, let’s edit build configuration.
Issue
Once we have added lint as dependency, we can easily proceed forward with writing our custom lint rule. Let’s write a simple Issue.
- Id: Id of the issue. This should be unique and is displayed in the report. You use this same id if you want to ingore a lint check.
- Description: Brief description of the issue.
- Explanation: Describe the issue in details and propose possible solutions.
- Category: This defines the category of the issue. There are many categories provided in lint tools such as
TYPOGRAPHY
,CORRECTNESS
, etc. You should choose the correct category based on your issue. - Priority: Define priority of the issue on a scale of 0 to 10.
- Severity: Define severity of the issue. eg.
WARNING
,ERROR
,FATAL
, etc.
Implementation
Implementation binds the issue to the detector class. We provide the detector class and a scope of the implementation. We provide Scope.RESOURCE_FILE_SCOPE
because the issue may be present in resources only.
Detector
We need to write detection logic for lint to detect the issue.
Android lint provides some scanning APIs to be used for detectors.
- UastScanner: Java + Kotlin files
- ClassScanner: Bytecode
- BinaryResourceScanner: Binary resources
- ResourceFolderScanner: Resource folders
- XmlScanner: Xml files
- GradleScanner: Gradle files
- OtherFileScanner: Other files in projects
Here, we extend ResourceXmlDetector
which implements XmlScanner
to scan Xml files and get Xml dom elements to perform checks on.
We override method getApplicableElements
and return TextView
because we just want to check for TextViews. Based on your requirements, you may return multiple element names or ALL
which would scan all the elements in xml.
visitElement
method is called when XmlScanner visits the applicable element. Here, we add our logic. We check if the TextView element has textAppearance
attribute or not. If not, we report the issue.
context.report
method is used to report the issue and generate a report. context.getLocation
gives the location of the element (file path, line number, column) to pinpoint the exact location in the report.
Registry
We have our Issue, Detecor and Implementation ready. We just need to register the issue and update gradle build script to include the registry class.
In getIssues
method, we return a list of all the issues that we want to register with Lint.
We also need to update build.gradle
file.
Setup
We are all ready with our custom lint rule. We just need to add it to the app’s build file. Update your app’s build.gradle
file and add following dependency.
That’s it! Let’s run lint.
./gradlew lintDebug
And your new custom lint rule will be used to check for issues.
Summary
Writing custom Lint rules for Android is super easy now and you should definitely write some of your own rules to avoid potential issues and especially to keep high code quality in a big team.
Series
- Get started with Android Lint - custom lint rules
- Android Lint Deepdive - advanced custom lint rules
Redux architecture and Android
Learn the Redux architecture and implement the clean architecture it in your Android codebase. This series of posts explores the depth of Redux and guides you through implementing the architecture in Kotlin for your android app.