Issue #257
checkstyle (Java)
Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. It automates the process of checking Java code to spare humans of this boring (but important) task. This makes it ideal for projects that want to enforce a coding standard.
app/build.gradle
apply plugin: 'checkstyle'
task checkstyle(type: Checkstyle) {
description 'Check code standard'
group 'verification'
configFile file('$project.rootDir/tools/checkstyle.xml')
source 'src'
include '**/*.kt'
exclude '**/gen/**'
classpath = files()
ignoreFailures = false
}
tools/chekcstyle
<?xml version="1.0"?><!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<module name="Checker">
<module name="FileTabCharacter"/>
<module name="TreeWalker">
<!-- Checks for Naming Conventions -->
<!-- See http://checkstyle.sourceforge.net/config_naming.html -->
<module name="MethodName"/>
<module name="ConstantName"/>
<!-- Checks for Imports -->
<!-- See http://checkstyle.sourceforge.net/config_imports.html-->
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size -->
<!-- See http://checkstyle.sourceforge.net/config_sizes -->
<module name="ParameterNumber">
<property name="max" value="6"/>
</module>
<!-- other rules ignored for brevity -->
</module>
</module>
findbugs (Java)
A program which uses static analysis to look for bugs in Java code
app/build.gradle
apply plugin: 'findbugs'
task findbugs(type: FindBugs) {
ignoreFailures = false
effort = "max"
reportLevel = "low"
classes = files("$project.buildDir/intermediates/javac")
excludeFilter = file("$rootProject.rootDir/tools/findbugs-exclude.xml")
source = fileTree('src/main/java/')
classpath = files()
reports {
xml.enabled = false
html.enabled = true
html.destination file("$project.buildDir/outputs/findbugs/findbugs-output.html")
}
}
tools/findbugs-exclude.xml
<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 classes (Dagger puts $ into class names) -->
<Match>
<Class name="~.*Dagger*.*"/>
</Match>
<!-- http://findbugs.sourceforge.net/bugDescriptions.html#ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD-->
<Match>
<Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" />
</Match>
</FindBugsFilter>
pmd (Java)
PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth
app/build.gradle
apply plugin: 'pmd'
task pmd(type: Pmd) {
ruleSetFiles = files("${project.rootDir}/tools/pmd-rules.xml")
ignoreFailures = false
ruleSets = []
source 'src'
include '**/*.kt'
exclude '**/gen/**'
reports {
xml.enabled = false
html.enabled = true
html.destination = file("$project.buildDir/outputs/pmd/pmd.html")
}
}
tools/pmd-rules.xml
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<exclude-pattern>.*/R.java</exclude-pattern>
<exclude-pattern>.*/gen/.*</exclude-pattern>
<rule ref="rulesets/java/basic.xml" />
<rule ref="rulesets/java/braces.xml" />
<rule ref="rulesets/java/strings.xml" />
<rule ref="rulesets/java/design.xml" >
<exclude name="AvoidDeeplyNestedIfStmts"/>
</rule>
<rule ref="rulesets/java/unusedcode.xml" />
</ruleset>
lint
Android Studio provides a code scanning tool called lint that can help you to identify and correct problems with the structural quality of your code without your having to execute the app or write test cases
app/build.gradle
android {
lintOptions {
lintConfig file("$project.rootDir/tools/lint-rules.xml")
htmlOutput file("$project.buildDir/outputs/lint/lint.html")
warningsAsErrors true
xmlReport false
htmlReport true
abortOnError false
}
}
tools/lint-rules.xml
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="GoogleAppIndexWarning" severity="ignore" />
<issue id="InvalidPackage" severity="error">
<ignore regexp="okio.*jar" />
<ignore regexp="retrofit.*jar" />
</issue>
<!-- 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>
Strict mode
import android.app.Application
import android.os.StrictMode
class App: Application() {
override fun onCreate() {
super.onCreate()
enableStrictMode()
}
private fun enableStrictMode() {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}
}
}
Version code
tools/grgit.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.ajoberstar:grgit:1.5.0'
}
}
import org.ajoberstar.grgit.Grgit
ext {
git = Grgit.open(currentDir: projectDir)
gitCommitCount = git.log().size()
}
task printVersion() {
println("Commit count: $gitCommitCount")
}
app/build.gradle
android {
defaultConfig {
versionCode gitCommitCount
}
}
Obfuscation
To make your app as small as possible, you should enable shrinking in your release build to remove unused code and resources. When enabling shrinking, you also benefit from obfuscation, which shortens the names of your app’s classes and members, and optimization, which applies more aggressive strategies to further reduce the size of your app
When you use Android Studio 3.4 or Android Gradle plugin 3.4.0 and higher, R8 is the default compiler that converts your project’s Java bytecode into the DEX format that runs on the Android platform
app/build.gradle
android {
buildTypes {
debug {
signingConfig signingConfigs.debug
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), '$project.rootDir/tools/proguard-rules-debug.pro'
}
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), '$project.rootDir/tools/proguard-rules.pro'
}
}
}
tools/proguard-rules.pro
-ignorewarnings
# Remove logs
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
tools/proguard-rules-debug.pro
-ignorewarnings
-dontobfuscate
-dontoptimize
-ignorewarnings
More proguard snippets https://github.com/krschultz/android-proguard-snippets
quality
tools/quality.gradle
task findbugs
task pmd
task checkstyle
app/build.gradle
apply from: "$project.rootDir/tools/quality.gradle"
File format
You don’t need to use ktlint or detekt to ensure that your code is formatted consistently. Simply enable “File is not formatted according to project settings” in the inspection settings.
ktlint (Kotlin)
An anti-bikeshedding Kotlin linter with built-in formatter
app/build.gradle
apply from: "$project.rootDir/tools/ktlint.gradle"
tools/ktlint.gradle
repositories {
jcenter()
}
configurations {
ktlint
}
dependencies {
ktlint "com.pinterest:ktlint:0.32.0"
// additional 3rd party ruleset(s) can be specified here
// just add them to the classpath (e.g. ktlint 'groupId:artifactId:version') and
// ktlint will pick them up
}
task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
classpath = configurations.ktlint
main = "com.pinterest.ktlint.Main"
args "src/**/*.kt", "--reporter=checkstyle, output=${buildDir}/outputs/ktlint.xml"
}
task ktlintFormat(type: JavaExec, group: "formatting") {
description = "Fix Kotlin code style deviations."
classpath = configurations.ktlint
main = "com.pinterest.ktlint.Main"
args "-F", "src/**/*.kt"
}
.editorconfig
[*.{kt,kts}]
indent_size=4
detekt (Kotlin)
Static code analysis for Kotlin
build.gradle
buildscript {}
plugins {
id "io.gitlab.arturbosch.detekt" version "1.0.0-RC14"
}
allprojects {}
tools/detekt.gradle
detekt {
toolVersion = "1.0.0-RC14"
input = files("src/main")
filters = ".*/resources/.*,.*/build/.*"
baseline = file("${project.rootDir}/tools/detekt-baseline.xml")
config = files(file("$project.rootDir/tools/detekt.yml"))
}
tools/detekt.xml
The intention of a whitelist is that only new code smells are printed on further analysis. The blacklist can be used to write down false positive detections (instead of suppressing them and polute your code base).
<SmellBaseline>
<Blacklist>
<ID>CatchRuntimeException:Junk.kt$e: RuntimeException</ID>
</Blacklist>
<Whitelist>
<ID>NestedBlockDepth:Indentation.kt$Indentation$override fun procedure(node: ASTNode)</ID>
<ID>TooManyFunctions:LargeClass.kt$io.gitlab.arturbosch.detekt.rules.complexity.LargeClass.kt</ID>
<ID>ComplexMethod:DetektExtension.kt$DetektExtension$fun convertToArguments(): MutableList<String></ID>
</Whitelist>
</SmellBaseline>
tools/detekt.yml
- https://github.com/arturbosch/detekt/blob/master/detekt-cli/src/main/resources/default-detekt-config.yml
- https://arturbosch.github.io/detekt/configurations.html
detekt uses a yaml style configuration file for various things:
autoCorrect: true
build:
maxIssues: 10
weights:
# complexity: 2
# LongParameterList: 1
# style: 1
# comments: 1
Run
./gradlew detekt
check
app/build.gradle
check.dependsOn 'checkstyle', 'findbugs', 'pmd', 'lint', 'ktlint', 'detekt'
Run
./gradlew check
Gradle Kotlin DSL
- How to use Gradle Kotlin DSL in Android https://github.com/onmyway133/blog/issues/285