Boost your Android App 3: Lint, An Essential Guide to Enhancing Code Quality with Your own Custom Rules
In previous articles, we discussed the CPU Profiler and Memory Management for boosting Android app performance. Now, we turn our focus to another crucial aspect in the realm of Android development: Lint. In the world of technology, enhancing software quality and developing sustainable projects is always at the forefront. Lint serves as a pivotal tool in this endeavor, particularly in Kotlin-based Android applications. This article delves into the importance, advantages, and impacts of employing Lint, providing a comprehensive understanding of its role in elevating the quality of Android development.
Understanding Lint
Lint is a tool that analyzes your code to identify potential errors, performance issues, usability problems, and compatibility issues. It is especially useful in large and complex projects, where it helps to catch errors, style issues, or potential risks that could be missed by the human eye in early stages.
Consequences of Not Using Lint
Critical problems like errors, performance issues, and security vulnerabilities can negatively affect the user experience and reliability of the application. Additionally, code reviews and team collaboration become challenging due to the lack of a standard coding style and quality control mechanism.
Before own Lint Rules, check linter tools
Let’s briefly explore two widely-used tools that significantly contribute to this process: ktlint and Detekt. We will proceed to the hands-on part of manually writing our own Lint rules, tailoring them to fit the specific needs and standards of our Kotlin-based Android projects.
The most used tools are;
We will delve into the fundamentals of lint and explore how libraries implement these rules behind the scenes.
Lint.xml Configuration
The lint.xml file in an Android project is used to configure the Lint tool’s behavior. By customizing lint.xml, you can control which checks Lint performs and how it responds to different issues.
Structure of lint.xml
The file typically starts with an XML header and contains a <lint> root element. Within this root element, you can define a series of <issue> elements. Each <issue> element corresponds to a specific check that Lint can perform.
Severity Levels
The severity attribute in <issue> defines how Lint treats each issue. There are several severity levels:
- fatal : Indicates critical problems that should be treated as errors. They will break the build if found.
- error : Non-critical but significant issues. They usually don't break the build but are important.
- warning : Less important issues, potential problems, or opportunities for improvement.
- informational: Mostly tips or recommendations, not really problems.
- ignore: Lint will completely ignore issues of this type.
Common Lint XML Issues
Some commonly used generic lint xml issues, and you can find(Image 4.0) yourself in xml as below, the ones suitable for your code.
<lint>
<issue id="UseSparseArrays" severity="warning"/>
<issue id="NewApi" severity="error"/>
<issue id="UnusedResources" severity="warning"/>
<issue id="HardcodedText" severity=""/>
</lint>
- Using SparseArray instead of Map<Integer, Object> optimizes memory usage on Android
- The NewApi, Indicates that the application uses a feature specific to an API level higher than the minimum API level.
- UnusedResources, Indicates unused resources (for example, drawable, layout files).
- HardcodedText, Indicates that hardcoded texts are used in the user interface.
Analyzing Code with Lint in Android Studio
- Click Analyze > Inspect Code… from Android Studio’s menu bar. (3.1)
- Select the project scope from the window that opens (for example, the entire project or a specific module). (3.2)
- Start the analysis by clicking the OK button. (3.2)
Output of Lint XML Analyze, (3.3) shows that the function getLazyColumnData can be made private. This suggests that the function is not accessed outside its defining class or file, and therefore, its visibility can be restricted to enhance encapsulation and reduce the API surface of the class.
Creating Custom Lint Rules
Custom Lint rules allow developers to enforce specific coding standards and practices that are unique to their project or organization. Here are some contexts where custom Lint rules are beneficial:
- Project-Specific Conventions
- Company-Wide Standards
- Performance Optimization
- Legacy Code Handling
By creating custom Lint rules, developers can automate the enforcement of these practices, making the code review process more efficient and reducing the likelihood of human error. This not only improves the quality of the code but also helps in maintaining a high standard of development practices within the team or organization.
Creating custom Lint rules in Android involves writing a Detector class that checks for specific conditions in your code, and an IssueRegistry class that registers these checks as issues.
1- Add Dependencies of lint
dependencies {
implementation 'com.android.tools.lint:lint-api:30.0.1'
implementation 'com.android.tools.lint:lint-checks:30.0.1'
}
2- Write the Detector Class
- Detectors are components of lint tools that specifically look for certain types of errors, code style violations, or potential problems. These often contain customized rule sets for specific programming languages or coding standards.
SourceCodeScanner
- Java/Kotlin source filesXmlScanner
- XML filesGradleScanner
-Gradle filesResourceFolderScanner
- resource folders
The getApplicableElements
function defines which elements are to be checked (e.g., Fragment and Activity classes in Android). The visitClass
function generates a report when it finds classes that don't follow this rule.
class NamingRulesConventionDetector: Detector(), SourceCodeScanner {
override fun getApplicableElements(): List<String> {
return listOf("androidx.fragment.app.Fragment", "android.app.Activity")
}
override fun visitClass(context: JavaContext, declaration: UClass) {
val name = declaration.name
if (name != null && !name.endsWith("Activity") && !name.endsWith("Fragment")) {
context.report(
ISSUE_NAMING_CONVENTION,
declaration,
context.getNameLocation(declaration),
"Class name should end with 'Activity' or 'Fragment'."
)
}
}
}
3- Implement Issue Registration
Inside the SourcesOfRules
class, an Issue
object named ISSUE_NAMING_CONVENTION
is created. This object defines problems identified during code analysis. In this example, it checks whether a class's name ends with "Activity" or "Fragment". The Issue object includes information like ID, brief description, detailed explanation, category, priority, severity, and implementation (i.e., which Detector
class to use and in which scopes it will operate).
class SourcesOfRules {
companion object {
val ISSUE_NAMING_CONVENTION = Issue.create(
id = "NamingConventionViolation",
briefDescription = "Classes should end with 'Activity' or 'Fragment'",
explanation = "This app's coding standards require that Activity and Fragment classes must end with 'Activity' or 'Fragment'.",
category = Category.CORRECTNESS,
priority = 5,
severity = Severity.ERROR,
implementation = Implementation(
NamingRulesConventionDetector::class.java,
EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
}
}
4- Register the Issue
class NamingConventionIssueRegistry : IssueRegistry() {
override val issues: List<Issue> get() = listOf(ISSUE_NAMING_CONVENTION)
}
5- Create own Registry Control
Practical Application
After integrating the custom rules, you can run Lint to analyze your project. This can be done through the terminal using the following command:
./gradlew :app:lint
This command triggers the Lint analysis process for your application module. Replace :app
with the appropriate module name if your setup differs.
Conclusion
Enhance code quality by enforcing consistent coding standards and detecting errors early, thus streamlining code reviews by allowing teams to focus more on logic and architecture rather than syntax. They integrate seamlessly into CI/CD pipelines, providing automated validation and immediate feedback, which prevents faulty builds and improves development cycles. This approach not only educates developers about best practices, leading to skill improvement, but also significantly reduces technical debt and boosts overall productivity by focusing efforts on critical development aspects rather than minor issues.
Improving Code Quality, Streamlining Code Reviews, Enhancing CI/CD Processes, Impacts on Development.
Key Point
Custom Lint rules ideas : Test Code Conventions, Comment and Documentation Standards, Security Checks, Thread Usage.
Please note that these rules will vary depending on the situation of you and your company.
Check the Github Repo https://github.com/basaransuleyman/CustomLint
Acknowledging the Source of Inspiration:
inspired ~ https://googlesamples.github.io/android-custom-lint-rules/api-guide/basics.md.html
For Jetpack Compose Fully Projects
Jetpack Compose with Hexagonal and Clean Architecture
Multi Module( Layer Based)with Clean Architecture & MVVM+MVIÂ
Boost Your Android App with CPU Profiling
Boost Your Android App with and Mastering with Memory Profiling