Android Tooling &

Code Analysis

Ken Baldauf

Software Engineer @ Originate

“It is not enough for code to work.” 

Robert C. Martin - Clean Code

Static Code Analysis

  • Analyze code quality without execution
  • Fast, efficient & customizable
  • Prone to false positives 

“It's not.” 

Anonymous iOS Developer

Is Code Analysis Important?

Importance of Code Analysis

  • Augment, but not replace tests
  • Find opportunities for refactoring and simplification
  • Detect easy to miss defects

Android/Java Analysis Tools

  • Android Lint
  • FindBugs
  • Infer
  • PMD 

FindBugs Gradle Setup

apply plugin: 'findbugs'

android.applicationVariants.all { variant ->
    task("findbugs${variant.name.capitalize()}", type: FindBugs) {
        ignoreFailures = false
        effort = "default"
        reportLevel = "medium"
        classes = files("$project.buildDir/intermediates/classes/${variant.dirName}")
        excludeFilter = file("$rootProject.rootDir/findbugs/exclude.xml")
        source = variant.javaCompile.source
        classpath = variant.javaCompile.classpath

        reports {
            // Only one of HTML or XML can be turned on at the same time
            html.enabled = true
            xml.enabled = !html.enabled
            xml.withMessages = true
            html.destination = 
             "$project.buildDir/outputs/reports/findbugs/findbugs-${variant.name}-output.html"
        }
        dependsOn "compile${variant.name.capitalize()}JavaWithJavac"
    }
    check.dependsOn(project.tasks.getByName("findbugs${variant.name.capitalize()}"))
}

FindBugs Custom Exclusions

<FindBugsFilter>

  <Match>
    <Class name="~.*R\$.*"/>
  </Match>
  <Match>
    <Class name="~.*Manifest\$.*"/>
  </Match>
  <Match>
    <Class name="~.*\$Module"/>
  </Match>
  <Match>
    <Class name="~.*\$\$BundleAdapter" />
  </Match>
  <Match>
    <Class name="~.*Model" />
    <Bug code="UuF,UwF,UrF" />
  </Match>

</FindBugsFilter>

FindBugs Report

Danger

Danger Systems

  • Automated code review
    • Fail builds or leave comments to enforce team norms
  • Supports:
    • Runs on: Circle, Travis, Jenkins, BuddyBuild, etc.
    • Comments on: GitHub, GitLab and Bitbucket
    • Diffs: Git

Danger Setup

gem 'danger'
gem 'danger-android_lint'
gem 'danger-todoist'
bundle install
bundle exec danger
DANGER_GITHUB_API_TOKEN 
DANGER_CIRCLE_CI_API_TOKEN

Danger File

android_lint.gradle_task = "app:lintStagingDebug"
android_lint.report_file = "app/build/reports/lint-results-stagingDebug.xml"
android_lint.lint

Danger File

username = ENV['CIRCLE_PROJECT_USERNAME']
project_name = ENV['CIRCLE_PROJECT_REPONAME']
build_number = ENV['CIRCLE_BUILD_NUM']
if username && project_name && build_number
  # submit message giving the coverage report that was generated by JacocoEverywhere
  message('[test coverage report](https://circleci.com/api/v1/project/'
    +username+'/'+project_name+'/'+build_number+'
    /artifacts/0/$CIRCLE_TEST_REPORTS/coverage/index.html)')
end

Danger File

# Make it more obvious that a PR is a work in progress and shouldn't be merged yet
warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"

# Warn when there is a big PR
warn("Big PR") if git.lines_of_code > 500

todoist.warn_for_todos
todoist.print_todos_table

Danger File

JIRA_LABEL_FORMAT = /\[OC-\d+\]/
JIRA_LINK_BASE = "https://ocandroid.atlassian.net/browse"

jira_links = github.pr_title.scan(JIRA_LABEL_FORMAT).map do |ticket|
   "<a href='#{JIRA_LINK_BASE}/#{ticket.tr("[]", "")}'>#{ticket}</a>"
end

if jira_links.any?
  message "<strong>JIRA Tickets:</strong> #{jira_links.join(', ')}"
end

Danger File

JIRA_LABEL_FORMAT = /\[OC-\d+\]/
JIRA_LINK_BASE = "https://ocandroid.atlassian.net/browse"

# Make it more obvious that a PR is a work in progress and shouldn't be merged yet
warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"

# Warn when there is a big PR
warn("Big PR") if git.lines_of_code > 500

android_lint.gradle_task = "app:lintStagingDebug"
android_lint.report_file = "app/build/reports/lint-results-stagingDebug.xml"
android_lint.lint

todoist.warn_for_todos
todoist.print_todos_table

username = ENV['CIRCLE_PROJECT_USERNAME']
project_name = ENV['CIRCLE_PROJECT_REPONAME']
build_number = ENV['CIRCLE_BUILD_NUM']
if username && project_name && build_number
  # submit message giving the coverage report that was generated by JacocoEverywhere
  message('[test coverage report](https://circleci.com/api/v1/project/'+username+'/'+project_name+'/'+build_number+'/artifacts/0/$CIRCLE_TEST_REPORTS/coverage/index.html)')
end

jira_links = github.pr_title.scan(JIRA_LABEL_FORMAT).map do |ticket|
   "<a href='#{JIRA_LINK_BASE}/#{ticket.tr("[]", "")}'>#{ticket}</a>"
end

if jira_links.any?
  message "<strong>JIRA Tickets:</strong> #{jira_links.join(', ')}"
end

Danger PR Comment

Questions?

Android Tooling & Code Analysis

By Kenneth Baldauf

Android Tooling & Code Analysis

From static code analysis to code coverage, an overview of a variety of Android & Java tools that make continuous integration easier and safer.

  • 671