Achieve High-Quality Android Code With Static Code Analysis Tools

These are tools that parse and analyze your source code without actually executing it. The goal is to find potential vulnerabilities such as bugs and security flaws and ensure conformance to coding guidelines. Helps to keep your code healthy and maintain code quality.

Think like static code analysis tools as an additional compiler that will run before the final conversion into the system language.

Benefits

  1. It helps to detect potential bugs that even manual or unit testing might have missed.
  2. Defines project-specific rules. For example, it helps to define a new project structure
  3. Helps you improve your knowledge of a new language.
  4. Scans your whole project, including files that you might not have ever read.

On Android, the most popular code analysis tools are:

  1. Android Lint
  2. Checkstyle
  3. Findbugs

Before starting the android integration, it would be great if a gist of what every tool does, should be provided. So, here it goes.

Android Lint

  1. Android lint is the one comes with Android Studio by default.
  2. It will check your project source files for identifying potential bugs and optimizations for usability, correctness, performance, security, accessibility, and internationalization.

FindBugs

  1. It analyses Java bytecode mainly the .classes to find any design flaw and potential bugs.
  2. It needs compiled code to work around and will eventually be fast since it works on bytecode level.
  3. The major sections in this tool are: Bad practice, Correctness, Multithreaded Correctness, Dodgy code, Performance Malicious, Code Vulnerability, Security Experimental and Internationalization

Checkstyle

  1. It basically analyses the source code of the project and looks to improve the coding standard by traversing over simple AST generated by Checkstyle.
  2. It verifies the source code for coding conventions like headers, whitespaces, formatting, imports etc.

How to set up?

All code analysis tools we’ll going to learn about in this tutorial are available as Gradle plugins, so we can create individual Gradle tasks for each of them. So we are going use a single Gradle file that will include them all. But before going to that, let’s create a folder that will contain all of our files for the static code analysis.

Open Android Studio and inside the app module, create a new directory named code_quality_tools. This folder will contain the XML files for the code analysis tools, and it will also have a Gradle file, quality.gradle, which will run our static analysis tasks for each tool.

Finally, visit build.gradle in the app module folder and include the below line at the end of the file:

apply from: '/code_quality_tools/quality.gradle'

Here, our quality.gradle Gradle script is being applied with a reference to its local file location.

Android Lint

To configure Lint, you have to include the lintOptions {} block in your module-level build. gradle file:

lintOptions {
   abortOnError false
  quiet true
  lintConfig file('./code_quality_tools/lint.xml')
}

The key Lint options we are concerned with are:

  1. abortOnError: whether lint should set the exit code of the process if errors are found.
  2. quiet: whether to turn off analysis progress reporting.
  3. lintConfig: the default configuration file to use.

Your lint.xml file can include issues you want Lint to ignore or modify, such as the example below:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
   <!-- Disable the given check in this project -->
   <issue id="IconMissingDensityFolder" severity="ignore" />
   <!-- Change the severity of hardcoded strings to "error" -->
  <issue id="HardcodedText" severity="error" />
</lint>

You can also run Lint by using Gradle tool window, opening the verification group in that, and then clicking on lint. Finally, you can run it via the command line as.

On Windows:gradlew lint
On Linux or Mac:./gradlew lint

Finally, a report will be generated after the task has finished executing, and is available at app module > build > outputs > lint-results.html.

Checkstyle

Checkstyle enforces the given rules you specify in an XML file by analyzing your project source code and compares them against known conventions or coding standards.

Checkstyle is an open-source tool and is well maintained by the community. This means you can create your own custom checks or modify existing ones to suit to achieve your need. For example, Checkstyle can perform a check on the constant names like final, static, or both in your classes. If the constant names do not obey a rule of being in uppercase with words separated by an underscore, the problem will be displayed in the final report.

// incorrect
private final static String myConst = "myConst";
// correct

Integrating Checkstyle

I will going to show you how to add Checkstyle into our Android Studio project and will demonstrate a practical example.

First, we need to create our coding rules. Inside checkstyle.xml (inside code_quality_tools directory), we create some Checkstyle configuration rules that will be run against our source code.

<?xml version="1.0"?>
<module name="Checker">

<!-- Checks for Naming Conventions-->
<!-- See https://checkstyle.sourceforge.net/config_naming.html -->
<module name="MethodName"/>
<module name="ConstantName"/>
<!-- Checks for Imports -->
<!-- See https://checkstyle.sourceforge.net/config_imports.html-->
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size -->
<!-- Seehttps://checkstyle.sourceforge.net/config_sizes -->
<module name="ParameterNumber">
<property name="max" value="6"/>
</module>
<!-- other rules ignored for brevity -->
</module>

In the above section, we include the rules or checks we want Checkstyle to validate in our source code. One rule is AvoidStarImport which, as the name says, it checks if your source code included an import statement like java.Utils.*. (Instead, you should explicitly specify the package to import, e.g. java.util.ArrayList.)

Take a look at other checks on the Checkstyle website.

To run this check over source code, we need to create a Gradle task. So visit the quality.gradle file and create a task called checkstyle like below:

apply plugin: ‘checkstyle’

task checkstyletask(type: Checkstyle) {
description 'Check code standard'
group 'verification'
configFile file('./code_quality_tools/checkstyle.xml')
source 'src'
include '**/*.java'
exclude '**/gen/**'
classpath = files()
ignoreFailures = false
}

Note that in the code above, we first applied the Checkstyle Gradle plugin. We gave it a description and added it to an already predefined Gradle group called verification.

The key properties of the Checkstyle Gradle task in the above task are:

  1. configFile: the Checkstyle configuration file to use.
  2. IgnoreFailures: whether or not to allow the build to continue if there are warnings.
  3. include: the set of include patterns.
  4. exclude: the set of exclude patterns. In this case, we don’t scan generated classes.

Finally, you can run the Gradle script by using the Gradle tool window on Android Studio, opening the verification group, and then clicking on checkstyletask to run the task.

You can use command line to execute checkstyle task

gradle checkstyletaskAfter finished running, a report will be created and is available at app module > build > reports > checkstyle. You can open checkstyle.html to view the report.

A Checkstyle plugin is available for Android Studio or IntelliJ IDEA. It offers real-time scanning of your Java files and is completely free.

FindBugs

FindBugs is also a free static analysis tool which analyses your class and looking for potential problems by checking your bytecodes against a known list of bug patterns. Some of them are:

  • Class defines hashCode() but not equals(): A class implements the hashCode() but not equals()—therefore both instances might be equal but not have the same hash codes. This falls under bad practice category.
  • Bad comparison of int value with long constant: The code is comparing an int value with a long constant that is outside the range of values that can be represented as an int value. This comparison will yield an unexpected result. This falls under the correctness category.

FindBugs is open-source, so you can contribute or monitor the progress of the source code on GitHub.   In the findbugs-exclude.xml file, if we want to prevent FindBugs from scanning some classes such as auto-generated resource classes and auto-generated manifest classes, they will do it by using regular expressions in our projects.

<FindBugsFilter>
 <!-- Do not check auto-generated resources classes -->
<Match>
<Class name="~.*R\$.*"/>
</Match>
<!-- Do not check auto-generated manifest classes -->
<Match>
<Class name="~.*Manifest\$.*"/>
</Match>
<!-- Do not check auto-generated Dagger classes-->
<Match>
<Class name="~.*Dagger*.*"/>
</Match>
<!-- 

https://findbugs.sourceforge.net/bugDescriptions.html#ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD–
>
<Match>
<Bug pattern=”ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD” />
</Match>
</FindBugsFilter>

And finally, we’ll include the findbugs task in quality.gradle:apply plugin: ‘findbugs’

task findbugs(type: FindBugs) {
description 'Run findbugs'
 group 'verification'
 classes = files("$project.buildDir/intermediates/classes")
source 'src'
classpath = files()
effort 'max'
reportLevel = "high"
excludeFilter file('./code_quality_tools/findbugs-exclude.xml')
reports {
xml.enabled = false
html.enabled = true
}
ignoreFailures = false
}

In the first line above, we applied FindBugs as a Gradle Plugin and then created a task called findbugs. The key properties of the findbugs task we are really concerned with are:

  1. classes: the classes to be analyzed.
  2. effort: the analysis effort level. The value specified should be one of min, default, or max.  Be aware that higher levels increase precision and find more bugs at the cost of running time and memory consumption.
  3. reportLevel: the priority threshold for reporting bugs. If set to low, all bugs are reported. If set to medium (the default), medium and high priority bugs are reported. If set to high, only high priority bugs are reported.
  4. excludeFilter: the filename of a filter specifying bugs to exclude from being reported, which we have created already.

You can then run the Gradle script by visiting the Gradle tool window, opening the verification group folder, and then clicking on findbugs to run the task. Or launch it from the command line:

gradle findbugs

A report will also be generated when the task has finished executing. This will be available at app module > build > reports > findbugs. The FindBugs plugin is another freely available plugin for download and integration with either IntelliJ IDEA or Android Studio.

apply plugin: 'findbugs'
task findbugs(type: FindBugs) {
description 'Run findbugs'
 group 'verification'
 classes = files("$project.buildDir/intermediates/classes")
source 'src'
classpath = files()
effort 'max'
reportLevel = "high"
excludeFilter file('./code_quality_tools/findbugs-exclude.xml')
reports {
xml.enabled = false
html.enabled = true
}
ignoreFailures = false
}

Leave a comment

Your email address will not be published. Required fields are marked *


captcha

X