Android Studio配置findbugs checkstyle pmd lint

findbugs checkstyle pmd lint

Posted by Felix on January 9, 2018

前言

在Android项目开发过程中,开发团队往往要花费大量的时间和精力发现并修改代码缺陷。Java静态代码分析工具能够在代码构建过程中帮助开发人员快速、有效的定位代码缺陷并及时纠正这些问题,从而极大地提高软件可靠性并节省软件开发和测试成本。本文将着重介绍findbugs、checkstyle、pmd、lint在Android Studio中的集成方法,并结合git hook检查提交的代码。

findbugs集成

在module下的build.gradle中添加以下代码

apply plugin: 'findbugs'


findbugs {
    //工具版本
    toolVersion = "3.0.1"
    //忽略失败,如果检测到bug,task会执行失败,这里设置true会让task继续执行
    ignoreFailures = false
    //分析等级:min  default   max
    effort = "max"
    //检测bug的等级:low   medium  high,等级越高检测越严格
    reportLevel = "high"
    //exclude Filter路径
    excludeFilter file('../config/quality/findbugs/findbugs-filter.xml')
}
task findbugs(type: FindBugs, group: 'verification') {
    description 'Run findbugs'
    //检测二进制文件路径
    classes = files("${project.rootDir}/${project.getName()}/build/intermediates/classes")
    source 'src'
    //匹配检测的文件名
    include '**/*.java'
    exclude '**/gen/**'
    reports {
        xml.enabled = false
        html.enabled = true
        //配置检查报告输出路径
        xml {
            destination "${project.rootDir}/${project.getName()}/build/reports/findbugs/findbugs.xml"
        }
        html {
            destination "${project.rootDir}/${project.getName()}/build/reports/findbugs/findbugs.html"
        }
    }
    classpath = files()
}

findbugs-filter的简单配置

<FindBugsFilter>
    <!-- http://stackoverflow.com/questions/7568579/eclipsefindbugs-exclude-filter-files-doesnt-work -->
    <Match>
        <Class name="~.*\.R\$.*"/>
    </Match>
    <Match>
    <Class name="~.*\.Manifest\$.*"/>
    </Match>

    <!-- All bugs in test classes, except for JUnit-specific bugs -->
    <Match>
        <Class name="~.*\.*Test" />
        <Not>
            <Bug code="IJU" />
        </Not>
    </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>

OK,配置完毕,运行./gradlew findbugs检查你的代码吧,如果你是首次集成,估计会检测出一堆不符合规范的代码,打开build/reports/findbugs/findbugs.html可查看详情,参考Bug Descriptions处理检测出来的问题,filter的详细配置可参考FindBugs Manul
注:findbugs检查的是二进制文件,需要编译完成后运行task。clean后直接运行注定失败,不要问为什么。。。

checkstyle集成

在module下的build.gradle中添加以下代码

apply plugin: 'checkstyle'


checkstyle {
    //工具版本
    toolVersion '6.5'
    //配置文件路径
    configFile file('../config/quality/checkstyle/checkstyle.xml')
    //filter路径
    configProperties.checkstyleSuppressionFilterPath = file(
            "${project.rootDir}/config/quality/checkstyle/suppressions.xml")
            .absolutePath
}

task checkstyle(type: Checkstyle, group: 'verification') {
    //检测代码路径
    source 'src/main/java'
    //排除项
    exclude '**/gen/**'
    exclude '**/test/**'
    exclude '**/androidTest/**'
    exclude '**/R.java'
    exclude '**/BuildConfig.java'

    //判断是否是git pre-commit hook触发的checkstyle
    //如果是,只检测要提交的java文件,否则检测路径下的所有java文件
    //如果不需要hook pre-commit,可移除本段代码,添加include '**/*.java'即可
    if (project.hasProperty('checkCommit') && project.property("checkCommit")) {
        def ft = filterCommitter(getChangeFiles())
        def includeList = new ArrayList<String>()
        for (int i = 0; i < ft.size(); i++) {
            String spliter = ft.getAt(i)
            String[] spliterlist = spliter.split("/")
            String fileName = spliterlist[spliterlist.length - 1]
            includeList.add("**/" + fileName)
        }
        if (includeList.size() == 0) {
            exclude '**/*.java'
        } else {
            include includeList
        }
    } else {
        include '**/*.java'
    }

    def configProps = ['proj.module.dir': projectDir.absolutePath]
    configProperties configProps

    // empty classpath
    classpath = files()
}

//过滤java文件
def filterCommitter(String gitstatusinfo) {
    ArrayList<String> filterList = new ArrayList<String>()
    String[] lines = gitstatusinfo.split("\\n")
    for (String line : lines) {
        if (line.contains(".java")) {
            String[] spliters = line.trim().split(" ")
            for (String str : spliters) {
                if (str.contains(".java")) {
                    filterList.add(str)
                }
            }
        }
    }
    return filterList
}

//获取git commit的文件列表
def getChangeFiles() {
    try {
        String changeInfo = 'git status -s'.execute(null, project.rootDir).text.trim()
        return changeInfo == null ? "" : changeInfo
    } catch (Exception e) {
        return ""
    }
}

//编译时先运行checkstyle,会略微降低编译速度
preBuild.dependsOn('checkstyle')

OK,checkstyle也配置完毕了,运行’./gradlew checkstyle’检查你的代码吧,又一堆不符合规范的代码迎面而来,打开build/reports/checkstyle/checkstyle.html可查看详情。
注:checkstyle.xmlsuppressions.xml由于篇幅原因就不贴代码了,文末给连接。

pmd集成

在module下的build.gradle中添加以下代码

apply plugin: 'pmd'


def configDir = "${project.rootDir}/config/quality"
def reportsDir = "${project.buildDir}/reports"

task pmd(type: Pmd) {
    //忽略失败,如果设置为true,检测出bug会停止task
    ignoreFailures = false
    //filter路径
    ruleSetFiles = files("$configDir/pmd/pmd-ruleset.xml")
    ruleSets = []
    //检测资源路径
    source 'src/main/java'
    //排除项
    exclude '**/gen/**'

    //判断是否是git pre-commit hook触发的pmd
    if (project.hasProperty('checkCommit') && project.property("checkCommit")) {
        def ft = filterCommitter(getChangeFiles())
        def includeList = new ArrayList<String>()
        for (int i = 0; i < ft.size(); i++) {
            String spliter = ft.getAt(i)
            String[] spliterlist = spliter.split("/")
            String fileName = spliterlist[spliterlist.length - 1]
            includeList.add("**/" + fileName)
        }
        if (includeList.size() == 0) {
            exclude '**/*.java'
        } else {
            include includeList
        }
    } else {
        include '**/*.java'
    }

    reports {
        xml.enabled = false
        html.enabled = true
        xml {
            destination "$reportsDir/pmd/pmd.xml"
        }
        html {
            destination "$reportsDir/pmd/pmd.html"
        }
    }
}

OK,pmd也集成完毕啦,运行./gradlew pmd检查你的代码吧,又一堆不合规范的警告铺面而来~打开build/reports/pmd/pmd.html可查看详情,点击每一个错误警告可跳转对应的说明,按照说明修改即可,如果想忽略某个错误警告,可参照pmd-ruleset.xml配置。
注:pmd.html同样在文末给出。

lint集成

在module下的build.gradle中添加以下代码

android {
    lintOptions {
        abortOnError false    //检查到错误时是否停止task
        xmlReport false
        htmlReport true
        //配置文件路径
        lintConfig file("$configDir/lint/lint.xml")
        //检测结果输出路径
        htmlOutput file("$reportsDir/lint/lint-result.html")
        xmlOutput file("$reportsDir/lint/lint-result.xml")
    }
}

OK,lint集成完毕,运行./gradlew lint检测代码吧。。。。。打开build/reports/lint/lint.html可查看详情。

同时运行

在module下的build.gradle中添加以下代码

check.dependsOn 'checkstyle', 'findbugs', 'pmd', 'lint'

运行命令./gradlew check

简单的集成方式

说了这么多,肯定不会让你一个个的去集成,简单的方法肯定是有的啦,参考QualitySample

  1. 下载QualitySample.zip,将config/拷贝到项目根目录
  2. build.gradle中添加apply from: '../config/quality.gradle'
  3. 运行./config/install.sh(添加git hooks)
  4. 没有第四步

至此,findbugs checkstyle pmd lint已经全部配置完毕,修改代码通过git提交时会先运行checkstyle和pmd检查提交的代码是否符合规范,只有符合规范才能通过完成提交。

总结

本文主要讲述findbugs、checkstyle、pmd、lint在android studio中的配置,提供了快速配置四种静态代码检查工具的方法,具体Filter的配置请参考文中给出的官方链接,不同项目不同团队对于代码的风格不尽相同,但是对于代码中存在的缺陷、性能问题、隐藏bug都是零容忍的,所以说Java静态代码检测工具尤为重要。