Skip to content

Improve Android code quality with static code analysis and runtime check with checkstyle, findbugs, PMD, Lint, StrictMode and LeakCanary

License

Notifications You must be signed in to change notification settings

BrianSpace/Android-Quality-Essentials

Repository files navigation

AndroidQualityEssentials [中文]

Improve Android code quality with static code analysis and runtime check:

It is recommended that you add these checks when you create a new project and fix the problems with every check-in (as part of your continuous integration process). Otherwise it would need enormous courage and patience when you face and fix the huge amount of errors.

Table of Contents

Get started

  1. Add the quality directory to your project.
    • You can either copy it:
      • Sync the code of this project and copy the quality directory to the root directory of your project.
      • Add the following line in your build.gradle:
      apply from: '../quality/static_analysis.gradle'
      
    • Or add as a remote module. Just like in my project Android-App-Architecture-MVVM-Databinding:
      • Add as a remote module:
      git remote add analysis https://github.com/BrianSpace/Android-Quality-Essentials.git
      • Then in your build.gradle:
      apply from: '../analysis/quality/static_analysis.gradle'
      
  2. Add Lint options
android {
    ...
    lintOptions {
        // Turn off analysis progress reporting by lint
        quiet true
        // Stop the gradle build if errors are found
        abortOnError true
        // Do not ignore lint warnings
        ignoreWarnings false
        // Treat warnings as errors
        warningsAsErrors true

        // Ignore rules list
        ignore 'GoogleAppIndexingWarning' // Remove this if the app support App Indexing
    }
    ...
}
  1. Add dependency for LeakCanary in your build.gradle:
 dependencies {
   ...
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
   ...
 }
  1. In your Application class (and not forget to add to the manifest):
public class AndroidQualityEssentialsApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectAll()
                    .penaltyDeath()
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectAll()
                    .penaltyDeath()   // If violations are in Android itself or 3rd-party libs, use penaltyLog.
                    .build());

            // Avoid the process dedicated to LeakCanary for heap analysis.
            if (!LeakCanary.isInAnalyzerProcess(this)) {
                LeakCanary.install(this);
            }
        }
        ...
    }
}
  1. Run gradlew check in the console to start the static analysis.
  • The analysis report will be located in the build/reports/ directory of the project in which you apply the static_analysis.gradle.
  1. Run your debug version application for runtime check.
  2. Add the check step to your Continuous Integration process.

Tips to fix issues

Allow member fields to begin with "m" letter

The naming convention defined in the style rules does not allow single letter prefix for member field names like "mMember". But if you like this style, you can change the format property of the MemberName module to "^[a-z][a-zA-Z0-9]$". Or change to "^m[A-Z][a-zA-Z0-9]$" to force the "m" prefix.

Add "final" to variables automatically

  1. In Android Studio, select "Analyze"->"Run Inspection by Name..." menu, search "be final", then run the following two rules under "Java"->"Code style issues":
    • Field may be 'final'
    • Local variable or parameter may be final
  2. After each run, in the result panel, click "Make final" button to add "final" automatically.

Create Utility Class

For PMD rule UseUtilityClass, it will prompt to create Utility Class if your class has only static fields and methods. You just need to:

  • Define your utility class as final.
  • Create a private constructor and throw exception in it to prevent instantiation. Sample:
public final class FileUtil {

    private static Context appContext;

    private FileUtil() throws InstantiationException {
        throw new InstantiationException("Utility class FileUtil should not be instantiated!");
    }

    public static void init(final Context context) {
        appContext = context.getApplicationContext();
    }

    /**
     * Get available cache directory. Prefer external over internal.
     */
    @NonNull
    public static File getAvailableCacheDir() {
        final File externalCacheDir = appContext.getExternalCacheDir();
        return externalCacheDir == null ? appContext.getCacheDir() : externalCacheDir;
    }
}

Trade-offs for PMD rules

All the static analysis rules are best practices from people's past experience, but they are not always truth that cannot be broken. Some of the PMD rules need to be based on your own preferences:

  1. AccessorMethodGeneration This rule prefer performance and reducing method count over encapsulation, usually I care more about encapsulation so I exclude this rule. But if you care more about performance and method count (to avoid Multi-dex), you can include it.
  2. GenericsNaming This rule prefer a single upper case letter for generic values. But I prefer some more meaningful names with "T" letter as the postfix, like ItemTypeT, for better readability. You can remove it from the "exclude" list if you like the single letter naming.

What will be checked?

Naming Conventions

Naming conventions are defined in the quality/checkstyle/naming_convention.xml file. The following rules are defined:

  1. Java files:
    • Should use Camel case.
    • Test files should be named "Test.java" or "Base.java".
  2. Resource files:
    • Resource files should use Snake case(lower case, concatenated by underscore).
    • Drawables should begin with "bg_", "ic_" or "img_".
    • Layouts should begin with "activity_", "fragment_", "view_", "dialog_", "item_" or "btn_".
    • Values should begin with "attrs_", "colors_", "dimens_", "strings_" or "styles_".

You can modify the regular expressions as you need for your own project. Run gradlew checkFileNames if you would like to check only the naming convention.

Code Styles with CheckStyle

CheckStyle is used to check the Java code style. Style rules are based on Google Java Style Guide with the following changes:

  • Severity: changed to "error".
  • Line length: changed to 120.
  • Indentation: tab size changed to 4.
  • MethodName: underscore is not allowed.
  • ConstantName: all upper case, underscore allowed.

Run gradlew checkCodeStyle if you would like to check only the code style. If you would like to exclude some files like 3rd party code, you can add an exclude item in the checkCodeStyle task in the static_analysis.gradle file.

Static Analysis with Findbugs

FindBugs scan your code for patterns that may result in bugs. The files to be excluded from the analysis is defined here. Run gradlew findBugs if you would like to run findbugs only.

Static Analysis with PMD

PMD is a static code analyzer that can detect common programming flaws. Rules are defined in quality/pmd/pmd-ruleset.xml. Full list of the rules can be found here. Run gradlew pmdCheck if you would like to run PMD only.

Static Analysis with Android Lint

Android Lint is a Android specific static code analysis tool. The full list of checks is here. Run gradlew lint if you would like to run Lint rules only.

Runtime check with StrictMode

StrictMode is very useful to detect slow operations in UI thread and resource leaks. Specifically, ThreadPolicy will detect disk/network I/O and slow operations in UI thead, whereas VmPolicy will detect resource leaks. For details, see the documents for ThreadPolicy.Builder and VmPolicy.Builder

Memory leak Detection with LeakCanary

LeakCanary can help to detect memory leaks. It has a very nice UI to report the leaks and show the whole reference chain so that you can easily locate where to fix the leaks.

Thanks

License

The MIT License

Copyright (c) 2017-2017 AndroidQualityEssentials project contributors

https://github.com/BrianSpace/AndroidQualityEssentials/graphs/contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

About

Improve Android code quality with static code analysis and runtime check with checkstyle, findbugs, PMD, Lint, StrictMode and LeakCanary

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages