I. Project Introduction:

In daily development, many specifications are superficial, relying on developers’ consciousness and code review. It is not safe to simply rely on human resources to check the code, and problems are often remedied. To ensure that the team develops specifications, custom Lint static code checking has recently been introduced into the project.

Android Lint is a static code checking tool provided by Google for Android developers. Lint can scan and inspect Android engineering code to find potential problems and alert programmers to fix them early. The most common yellow warning area in a block of code in development is when Lint is in action.

Android natively provides hundreds of Lint apis for developers to use, but if you want to tailor your business specification, you must use custom Lint.

Ii. Project Background

Because the company project was old and had been iterating for a long time, different developers had their own code habits, leading to inconsistent code styles, so they wanted to standardize their code by using Lint.

There was a security requirement to replace the system.out. print and native Log classes in the project with the unified Log printing utility class output, and to prompt developers to use the defined MMKV utility class when using SharedPrefrences. Or use Glide or some other third-party framework instead of BitmapFactory to create bitmaps, etc. Since these are very business-dependent Lint specifications, there is only a brief description of how to implement a custom Lint and a hint of potholes to be aware of.

Iii. Practice Process:

Given the lack of official documentation, the fact that many Chinese blogs on the web are years old, and the relatively frequent updates to Lint’s API, there have been some bumps along the way.

Custom Lint starts by understanding the main apis involved:

1. Issue: Each Issue represents a Lint rule. 2. Detector: Used to detect and report the issues in the code, each Issue must be specified by Detector. Scope: Declare the Scope of code that the Detector will scan, such as JAVA_FILE_SCOPE, CLASS_FILE_SCOPE, RESOURCE_FILE_SCOPE, GRADLE_SCOPE, etc. An Issue can contain one to multiple scopes. 4. Scanner: Used to scan and find issues in code, each Detector can implement one to multiple scanners. 5. IssueRegistry: Entry to Lint rule loading, providing a list of issues to examine.Copy the code

So let’s get started

1. Create a Java project

Add dependencies to the build.gradle file in the root directory

apply plugin: 'Java' dependencies {/ / using compileOnly avoid relying on conflict compileOnly "com. Android. Tools. Lint: lint - API: 27.1.2" / / introduction of lint - checks, You can view the source code in Android Studio. CompileOnly com. Android. "tools. Lint: lint - checks: 27.1.2"}Copy the code

Note the first pit here, if the Gradle plugin is X.Y.Z, the version of the Lint library needs to be X+ 23.y.z. For example, if the Gradle plugin version in the project is 4.1.2, I will introduce version 27.1.2 of Lint. If it doesn’t match your demo, it may not work

2. Create Detector & ISSUE

We’ll start by implementing an Detector that scans the code, finds problems and reports them.

An ISSUE is a description of a problem, including priority, details of the problem, proposed fixes, scope to be checked, etc. It is worth mentioning that the compilation process is aborted if an ERROR with a severity of ERROR is detected.

Dector also needs to implement the Scanner interface to indicate the type of scan. Scanner has undergone the replacement of JavaScanner -> JavaPsiScanner -> UastScanner. This is why a lot of times the blog examples don’t work, but we also recommend using the latest UastScanner as it also supports Kotlin.

Here is an example of a Dector implementation of log checking:

Public Class LogUtilDetector extends Detector implements Detector.UastScanner{// Custom ISSUE public static final ISSUE ISSUE = issue.create ("LogUse", // unique ID "Avoid using Log or system.out.println ", // ISSUE details" please use LogUtil wrapped in project, SECURITY, // Problem type 5, // Priority severity.warning, New Implementation(LogUtilDetector. Class, Scope.JAVA_FILE_SCOPE); new Implementation(LogUtilDetector. Class, Scope. Public List<String> getApplicableMethodNames() {return array.asList ("d", "e", "I ", "v", "w"); Public void visitMethod(JavaContext Context, JavaElementVisitor Visitor, PsiMethodCallExpression node, PsiMethod method) { JavaEvaluator evaluator = context.getEvaluator(); If (evaluator. IsMemberInClass (method, "android. Util. Log")) {String message = "please use the project wrapped in good LogUtil"; context.report(ISSUE, node, context.getLocation(node), message); }}}Copy the code

3. The registration of the Detector

Create a RegistryActivity inheritance IssueRegistry, and then register the LogUtilDetector implemented in the previous step. This is the entry point for the custom Lint tool.

public final class RegistryActivity extends IssueRegistry { @Override public List<Issue> getIssues() { return Arrays.asList( LogUtilDetector.ISSUE ); }}Copy the code

4. Introduce custom Lint

Add the following code to your Lint Module’s build.gradle file:

jar {
    manifest {
        attributes("Lint-Registry-v2": "RegistryActivity")
    }
}
Copy the code

The above key lint-registry -v2 is fixed, and the following value needs to be changed according to your actual IssueRegistry class name.

Then add to build.gradle for the module you want to check:

dependencies {
    lintChecks project(":lint_lib")
}
Copy the code

From here you can execute custom Lint tasks from the command line.

Finally, if you add Lint dependencies to a module using compileOnly, that module will only be checked for code and will not be passed into the release package.

Iv. Summary thinking:

To summarize, custom Lint can be used to check static code specifications in a project, but it’s up to developers to raise spec awareness to fundamentally improve code quality. For example, summarize the standard development documents, improve the testing process, etc.

The native Lint provided by Android is also worth learning from. What to do if you have an idea but don’t have a good implementation: Take a look at the BuiltinIssueRegistry class to see how the Lint source code is implemented.

Points for improvement:

  1. Custom Lint rules can check the entire project, whereas for older projects, a lot of historical code didn’t have on-line problems but didn’t conform to our custom Lint specification. At this time, it is risky to change the code rashly, and developers are required to be familiar with the code, which also increases the workload of developers invisibly.

  2. One thing that can be improved to address the above problem is to use incremental checking, limiting the checking to newly committed code and avoiding changes to “ancestral” code.

  3. For problems that the team is concerned about, consider raising the Severity level to severity.error, which is a technical way to limit non-standard code. As a developer, focus on high priority issues first and ignore low priority issues. For high-priority problems, ensure that they are solved first. If developers have a uniform approach to all priorities, then grading is meaningless.

  4. After stable operation, it can be considered to publish to remote warehouse or local warehouse. Further, it can be considered to realize unified standards of lintOptions and other configurations through plugin.

  5. Lint checks for time-consuming optimizations, checks for broken statistics, and other features.

Reference article:

  1. Android Official Article

  2. Meituan Takeaway Android Lint code checking practices


This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign