×

Android 项目代码质量保证实践

96
zyl06
2017.02.11 23:41* 字数 6021

1 背景

一个项目开发必然会涉及团队协作,而工程质量就需要团队去保证。一般我们期望的代码:无潜在风险、无重复逻辑、风格无差异、可阅读性好、新人上手速度快等。为了达到上述目标,一般团队都会制定一套自己认可的编码规范,并且周期性进行 code review。然而编码规范的制定,那么一套编码规范需要包含哪些内容,另外编码规范仅仅是一套软规范,实际程序员同学能遵守到什么程序还是一个未知数,所以需要我们进行人肉 code review,而这种人肉排查方式,很容易遗漏部分问题,保障性还是有些不足。

为此,我们从编码前期、编码中期和编码后期保证进行了初步尝试。

2 编码前期 - 编码规范

对于一个 Android 项目,一般需要涉及的编码规范有:

  1. 普通 java 编码风格规范

    如每个方法最大行数,每个类文件的最大行数,每个方法最大参数数等

  2. 普通 java 编码最佳实践

    iffortry 等嵌套深度规范,变量初始化规范等

  3. 通用 Android 编码规范(java 部分和 xml 部分),

    包含 Android java 部分和 Android xml 部分,如避免使用普通内部类定义handler,避免 layout xml 中存在无用结点等

  4. Gradle 编码规范

    如尽量避免 lib 使用 module,独立工程可以通过 aar 或 mvn 方式导入

  5. 具体项目相关的编码规范等

    如项目团队规定使用自定义 LogUtil 打日志,Activity、Fragment 等重要类的继承关系,Activity 对应的 xml 文件必须以 activity_ 开头等

制定了这些内容可以一定程度上规范程序猿的编码,配合团队进行了周期性的 code review (一般是一个版本一次,大概 4 个星期一次),会有比较好的效果。然而即使这么做,还是存在一定的问题,距离我们期望的目标还是比较远。比如各个单例类的定义五花八门,使用 LogUtil 代替 Log 的使用,Message.Obtain() 代替 new Message()Activity 部分文件命名,甚至 ActivityFragment 的基类定义规则还是很容易发生错误,并没有被发现。随着编码规范的完善充实,多个开发的编码规范如何保证,就会成为一个显而易见的问题。

3 编码中期 - 编码模板

为了实现公用代码复用,我们定义了一些 util 工具类,但随着各个开发的补充,这套 util 工具类也越来越多,如 LogUtilKeyboardUtil 等,而这些类一部分是为了统一入口,如统一使用 LogUtil,可以统一做到测试服打开本地日志,线上服关闭日志;KeyboardUtil 方便使用者控制键盘的弹出隐藏等操作。虽然定义了这些工具类,但终究存在应该使用而没有使用的情况。当然这些工具代码并不难,开发在自己的模块也能很容易的实现和使用,一般也不会出问题。然而上述讲的优点都会消失掉。而这些问题依赖 code review 也是件头疼的问题。

此外,RecycleView 的编码方式,单例模式的实现方式等等,各个开发可能写出各式代码,甚至实现的单例模式并不是线程安全的。

提了这么多,另一方面,要求开发在繁忙的业务中严格遵守这些规范,也有些强人所难。所幸,Android Studio 为我们提供了编码模板来解放开发的工作,并一定程度上统一编码风格。

3.1 Live Template

3.1.1 系统模板

查看一个使用 Android Studio 中很常见的例子,输入 for,出现下拉列表如下:

image

当选择 fori,出现编码片段:

image

确认循环变量 i,跳入循环结果值的输入:

image

上例,就是 Android Studio 中系统提供的 Live Template 一个实例。这个类似于 iOS 中的 Code Snippets,提供了代码片段的能力。

Android Studio (Mac) 进入 Settings/Preferences -> Editor -> Live Templates,可以看到已定义的模板组:

image

查看 fori 编码模板的实现:

image
  • A: 完成模板的快捷键 Tab
  • B: 位置分类 iterations
  • C: 编码模板对应的缩写 fori
  • D: 模板的内容
  • E: 模板应用环境

3.1.2 自定义模板

  1. 构建项目 group

    image

    输入 group 的名称

    image
  2. 构建具体编码模板

    image
    • Abbreviation:触发编码模板的缩写
    • Description:模板的具体描述
    • Template Text:具体的模板内容
  3. 变量定义

    变量形式为 $<variable_name>$,点击 Edit variables 可设置变量具体内容:

    image
    • Name:变量名
    • Expression:变量表现
    • Default value:默认值
    • Skip if defined:是否跳过编辑已经定义的值
  4. 模板应用环境

    点击 No application contexts yet. Define,设置为 java 环境:

    image
  5. 模板文件

    构建了模板 group 后,在 android studio config\templates 目录下查看到 yanxuan.xml

    windows: C:\Users\\<user>\\.AndroidStudiox.x\config\templates(user 为你的计算机用户名)

    mac:~/Library/Preferences/AndroidStudiox.x/templates

    <templateSet group="test">
      <template name="yxtest" value="testMethod($a$, $b$);" description="这是一个测试模板" toReformat="false" toShortenFQNames="true">
        <variable name="a" expression="lineNumber()" defaultValue="2" alwaysStopAt="false" />
        <variable name="b" expression="" defaultValue="" alwaysStopAt="true" />
        <context>
          <option name="JAVA_CODE" value="true" />
          <option name="JAVA_STATEMENT" value="true" />
          <option name="JAVA_EXPRESSION" value="true" />
          <option name="JAVA_DECLARATION" value="true" />
          <option name="JAVA_COMMENT" value="true" />
          <option name="JAVA_STRING" value="true" />
          <option name="COMPLETION" value="true" />
        </context>
      </template>
    </templateSet>
    
  6. 设置完毕,实践查看:

    image

    yxtest

    image

    singleton

3.2 Android Studio Template

  1. 背景

    除了 Live Template 之外,工程项目中很多新建的类也有很多机械的代码,如我们定义的 Activity 要么继承自 BaseBlankActivity,要么继承自 BaseActionBarActivity,另外项目中采用 MVP 模式,因此一个 Activity 基本上会有一个对应的 presenter 类,一个 layout 文件,同时很多时候,一个页面中会有一个需要支持刷新的 RecycleView 等。除此之外,ViewHolderHttpTask 等代码也是固定模式的代码。

    这些都是固定机械的代码,而如果是人肉去写的话,难免会出现代码风格不一致、不规范的情况,同时也浪费了一部分的时间。所幸,Android Studio 提供了工程类模板,方便我们实现这样的功能。

  2. 系统模板

    查看 Android Studio 系统类模板,我们能发现有很多定义好的类模板:

    image

    如需要创建一个空的 Activity 页面,可以选择 Empty Activity,并填写类名,layout 名称等信息,之后就能出现对应的添加或修改:MainActivity.javaactivity_main.xmlAndroidManifest.xml

    image

    而这些模板定义,可以在相关路径文件中找到:

    • Windows: ${Android Studio 的安装目录}/plugins/android/lib/templates/
    • Mac: Android Studio.app/Contents/plugins/android/lib/templates/
    image

    针对 EmpytActivity 这里需要定义的文件有:

    • globals.xml.ftl:定义当前模板的一些全局变量
    • recipe.xml.ftl:定义模板拷贝的逻辑等
    • template.xml:定义模板对话框的样式
    • template_blank_activity.png:定义模板的图标
    • root/src/app_package/SimpleActivity.java.ftl:具体的模板文件
    image16

    图片来自:http://www.slideshare.net/murphonic/custom-android-code-templates-15537501

  3. 自定义模板

    而针对我们需要自定义的模板,可以在模板定义路径下新建文件夹和文件即可,细节内容可查看 Tutorial How To Create Custom Android Code Templates

    项目的模板文件内容:

    image17

    设置完模板文件之后,重启 Android Studio,可以生效模板文件,使用模板文件如下:

    image18

4 编码后期 - 静态代码检查

由上,我们定义了编码规范,定义了 Live Template 和 Android Studio Template 方便程序猿更好的准守我们的项目编码规范。然后编码规范毕竟只是软规范,而提供编码模板更多的解决大量 util 的使用问题和便利小伙伴完成机械编码,并不能完全保证程序猿严格按照全部的规范来编码。

为此,我们需要一套静态代码检查机制能检查已有的代码是否遵守规范。总结已有的规范,可以将规范类型归纳为普通 Java 规范、普通 Android 规范、具体项目规范等。而这些检查点,可以配合不同的检查工具进行检查。

4.1 检查 java 代码风格 - CheckStyle

对于 java 规范,checkstyle 帮助开发者实现常用的检查。这里 CheckStyle 能检查的内容有:

  1. Javadoc 注释
  2. 命名约定
  3. 标题
  4. Import 语句
  5. 体积大小
  6. 空白
  7. 修饰符
  8. 代码问题
  9. 类设计
  10. 混合检查(包活一些有用的比如非必须的System.out和printstackTrace)

检查内容很多,而检查项需要和具体的项目规范做结合。如,每行代码字符数控制在 80,单页代码行数控制在 800 等。因此需要结合配置文件,来检查项目中的 java 代码。在 Android Studio 上配置 CheckStyle 流程如下:

  1. 在 Android Studio 中添加 gradle Plugin

    apply plugin: 'checkstyle'
    
  2. 设置 CheckStyle 版本

    checkstyle {
        toolVersion '6.1.1'
        showViolations true
    }
    
  3. 配置 CheckStyle 检查项

    task checkstyle(type: Checkstyle) {
        configFile file("$configDir/checkstyle/checkstyle.xml")
        configProperties.checkstyleSuppressionsPath = file("$configDir/checkstyle/suppressions.xml").absolutePath
        source 'src'
        include '**/*.java' // 检查 java 代码
        exclude '**/gen/**' // 排除生成的代码
        classpath = files()
        ignoreFailures true  // 忽略检查失败的情况,避免gradle命令执行中止
    }
    
  4. 配置自定义的检查项:

    checkstyle.xml

    <!--单个文件方法数上限最多为 30-->
    <module name="MethodCount">
        <property name="maxTotal" value="30"/>
    </module>
    <!--方法名的首字母是小写-->
    <module name="MethodName">
        <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
    </module>
    <!--静态变量名的首字符是 s-->
    <module name="StaticVariableName">
        <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
        <property name="applyToPublic" value="true"/>
        <property name="applyToProtected" value="true"/>
        <property name="applyToPackage" value="true"/>
        <property name="applyToPrivate" value="true"/>
    </module>
    <!--只有私有构造函数的类需要定义成 final 类型-->
    <module name="FinalClass"/>
    
    ...
    

    具体其他的检查项配置可以查看 检查配置链接

  5. 执行 checkstyle 检查

    ./gradlew checkstyle
    
  6. 查看检查结果

    命令执行结束,查看检查结果文件:${project}/app/build/reports/checkstyle/checkstyle.html

    image

4.2 检查 java 代码质量 - FindBugs

CheckStyle 工具不同的是,FindBugs 不注重样式或者格式,而是试图寻找出真正的缺陷或者现在的性能问题。FindBugs 检查类和 Jar 文件,不是通过分析类文件的形式或结构来分析程序,而是使用 Visitor 模式,将字节码与一组缺陷模式进行对比以发现可能的问题。而这些问题比如如下:

  1. 忽略返回值

    image

    上述代码执行结束之后,并没有什么意义,变量 a 的值也不会变成 dddbbbccc。因此,上述代码很可能是程序猿的 bug。为此 FindBugs 能找出这种问题

  2. 空指针示例

    image

    上述最后一行代码,很明显在执行的时候会发生空指针异常,这里因为 FindBugs 无法知道变量 strMaps 是否确实有 aaa 这个 key,为此这里会检查出错误。

  3. 未初始化的成员变量使用

    image

    这里由于类成员变量 actions 并未初始化,因此当 actions.add("TEST") 被执行的时候会发生异常。

Android Studio 上 FindBugs 的集成如下:

  1. 在 gradle 中引入插件

    apply plugin: 'findbugs'
    
  2. 在 gradle 中配置 findbugs task

    task findbugs(type: FindBugs, dependsOn: "assembleDebug") {
        ignoreFailures = false
        effort = "max"
        reportLevel = "high"
        excludeFilter = new File("$configDir/findbugs/findbugs-filter.xml")
        classes = files("${project.rootDir}/app/build/intermediates/classes")
    
        source 'src'
        include '**/*.java'
        exclude '**/gen/**'
    
        reports {
            xml.enabled = false
            html.enabled = true
            xml {
                destination "$reportsDir/findbugs/findbugs.xml"
            }
            html {
                destination "$reportsDir/findbugs/findbugs.html"
            }
        }
    
        classpath = files()
        ignoreFailures true  // 避免检查失败 gradle 执行中止
    }
    
  3. 执行 findbugs 检查

    ./gradlew findbugs
    
  4. 查看检查结果

    查看检查结果文件:${project}/app/build/reports/findbugs/findbugs.html

    image

4.3 检查 Android 代码质量 - Lint

4.3.1 基本介绍

前面 FindBugs 的检查实例(忽略返回值, 未初始化的成员变量使用),可以发现在 Android Studio IDE 上,已经出现了标黄提示,我们把光标放上去,就能看到具体的提示了:

image

cmd + F1 可以看到具体的错误提示:

image

这就原生 Lint 给我们提供的错误提示功能。除了和 FindBugs 重复的纯 java 代码检查之外,Lint 能检查很多其他工具无法检查的内容,也更贴合 Android:

image

在 Activity 内定义非静态内部类 Handler 的报警

image

AndroidManifest.xml 中定义 export 为 true 的广播接受器,但没有定义权限,Lint 检查认为是不安全的

image

build.gradle 文件中引用的 support 包的版本低的提示

Android Lint 是一个静态代码检查工具,能够对潜在的 bug,可能的安全性、性能、可用性、可访问性、国际化等优化内容做出监测:

image
image

来自官方文档 Improve Your Code with Lint

  1. App Source Files:工程中的源文件,包括 java 代码、资源 xml 代码、gradle 文件,图片资源文件以及 progroud 等文件
  2. lint.xml:配置文件,配置哪些 lint 检查可以排除,自定义问题的严重级别
  3. lint Tool:静态代码检查工具,可以是命令行或者集成在 Android Studio 上
  4. lint Output:代码检查结果,可以直接显示在 Console 上,也可以是 lint-result.html

4.3.2 原生 Lint 检查

在 Android SDK Tools 16 及更高的版本中,Lint 工具会自动安装。原生 Lint 的检查项已经有 200 多项 (包括前面示例的 5 项内容),因此使用原生的功能点,就能检查开发中的大部分通用问题。

  1. Android Studio IDE 上配置 Lint 检查偏好设置

    (Mac 下) PreferencesEditorInspections 进入 Android StudioLint 配置界面

    image
    • A:配置 Profile,方便不同项目或者不同情况下使用不同的 Profile 进行检查
    • B:Lint 检查点 Group
    • C:Lint 检查点具体描述
    • D:Lint 检查点警告级别设置
    • E:Lint 检查点开关
  2. lint.xml 上配置 Lint

    除了可以通过 IDE 配置 Lint,还可以通过直接 lint.xml 为单个项目配置检查规则

    <?xml version="1.0" encoding="UTF-8"?>
    <lint>
        <!-- Disable the given check in this project -->
        <issue id="IconMissingDensityFolder" severity="ignore" />
    
        <!-- Ignore the ObsoleteLayoutParam issue in the specified files -->
        <issue id="ObsoleteLayoutParam">
            <ignore path="res/layout/activation.xml" />
            <ignore path="res/layout-xlarge/activation.xml" />
        </issue>
    
        <!-- Ignore the UselessLeaf issue in the specified file -->
        <issue id="UselessLeaf">
            <ignore path="res/layout/main.xml" />
        </issue>
    
        <!-- Change the severity of hardcoded strings to "error" -->
        <issue id="HardcodedText" severity="error" />
    </lint>
    

    来源 Android Develop 文档 Improve Your Code with Lint

  3. gradle 中配置 Lint task

    android {
        lintOptions {
            abortOnError false // 配置 lint 过程中出错,不中止 gradle 任务
            xmlReport false
            htmlReport true
            lintConfig file("$configDir/lint/lint.xml") // 配置 lint 检查规则
            htmlOutput file("$reportsDir/lint/lint-result.html") // 配置 lint 输出文件
            xmlOutput file("$reportsDir/lint/lint-result.xml") // 配置 lint 输出文件
        }
    }
    
  4. 执行检查

    在工程根目录执行以下命令 (Mac),以执行检查任务

    ./gradlew lint
    
  5. 检查结果

    生成的检查结果在 ${项目工程}/app/build/reports/lint/lint-result.html

    image

4.3.3 自定义 Lint 检查

虽然原生的 Lint 检查已经很强大了,检查项也已经很多,然而还是无法满足项目中的特有需求:

  1. log 统一使用 LogUtil
  2. 对应 Activity 的 layout 命名为 activity_XXX
  3. 对应 Fragment 的 layout 命名为 fragment_XXX
  4. Activity 必须派生自 BaseBlankActivityBaseActionBarActivity

对于以上这些需求,原生 Lint 检查(包括 CheckStyleFindBugs)就已经无能为力了,我们必须编码支持自定义检查。以项目中集成的 Lint 检查为例,讲述流程:

4.3.3.1 配置 Gradle,引入 lint
dependencies {
    ...
    compile 'com.android.tools.lint:lint-api:24.5.0'
    compile 'com.android.tools.lint:lint-checks:24.5.0'
}
  • lint-api: 官方给出的API,API并不是最终版,官方提醒随时有可能会更改API接口。
  • lint-checks:已有的检查。
4.3.3.2 定义 IssueRegistry

新建一个 MyIssueRegistry 类,继承自 IssueRegistry。用来注册我们自定义的全部 issue

public class MyIssueRegistry extends IssueRegistry {
    @Override
    public List<Issue> getIssues() {
        System.out.println("********YXLint rules works!!!********");
        return Arrays.asList(
                LogUsageDetector.ISSUE,
                ToastUsageDetector.ISSUE,
                ActivitySuperClassDetector.ACTIVITY_SUPER_CLASS_ISSUE,
                ...
                BuildGradleVersionDetector.ISSUE);
    }
}

其中:

  • LogUsageDetector.ISSUE:用于检查不允许直接使用 Log.* 方式输出本地日志的代码
  • ToastUsageDetector.ISSUE:用于检查直接用 Toast 方式显示 toast 的代码
  • ActivitySuperClassDetector.ACTIVITY_SUPER_CLASS_ISSUE:用于检查 Activity 的基类
  • BuildGradleVersionDetector.ISSUE:用于检查 gradle 文件中不允许直接写数字版本号的代码
4.3.3.3 gradle 清单项中注册前面定义的 IssueRegistry
jar {
    manifest {
        attributes('Lint-Registry': 'com.netease.htlint.lintrules.MyIssueRegistry')
    }
}
4.3.3.4 定义 Detector

MyIssueRegistry 类中声明注册了各个 DetectorIssueIssueDetector 发现并报告,是 Android 程序代码可能存在的风险。而这里就需要真正实现这些 Detector,以检查 Activity 的基类为例。

image

ACTIVITY_SUPER_CLASS_ISSUE 这个 Issue 的定义需要使用 Issue.create(...) 方式实现,同时需要传入 6 个参数分别如下:

  • A:一个固定的唯一的 id 代表这个 Issue
  • B:对于问题的简短总结,描述问题而不是修复措施
  • C:完整的问题解释和修复建议
  • D:问题类别,现在已有的问题类别有如下
    • Lint
    • Correctness (incl. Messages)
    • Security
    • Performance
    • Usability (incl. Icons, Typography)
    • Accessibility
    • Internationalization
    • Bi-directional text
  • E:优先级,必须在1到10之间,10为最重要/最严重。
  • F:严重级别,可选值有 Fatal, Error, Warning, Informational, Ignore
  • G:为 IssueDetector 提供映射关系,Detector 就是当前类。声明扫描检测的范围 Scope,描述 Detector 需要分析时需要考虑的文件集,包括:Resource 文件或目录、Java 文件、Class 文件

ActivitySuperClassDetector 继承自 Detector,并实现 Detector.JavaScaner。这里主要自定义实现的方法如上图 H,I

  • H:检查类的基类是 "android.support.v4.app.Activity" 或 "android.app.Activity"
  • I:具体检查类的方法,这里需要排除非 yanxuan 包名下的代码,判断当前类的基类是否是 BaseBlankActivityBaseActionBarActivity?如果都不是的话,则报告错误
  • J:报出问题的方法:该方法中指定参数有:
    • 需要报错的 Issue
    • 发生问题的代码在语法树上的节点
    • 发生问题的代码位置
    • 警告的信息
4.3.3.5 生成 jar 包

完成上述步骤,可以在控制台中通过命令 ../../gradlew assemble 来执行编译任务,就可以输出我们需要的 jar 文件 (htlintrules_jar-0.0.1.jar) 了

4.3.3.6 jar 的使用

按照 Google 方法,可以将 htlintrules_jar-0.0.1.jar 拷贝到 ~/.android/lint 中,但缺点是针对会影响一台机器其他的工程。很明显,我们的自定义 Lint 检查有很多是项目中特有的一些编码规范。

为此,我们采用 LinkedIn 方案:将 jar 放到一个 aar 中。这样我们就可以针对工程进行自定义 Lint,lint.jar 只对当前工程有效。

在现有的 htlintrules_jar 工程的 build.gradle 中添加代码,整体看起来如下:

apply plugin: 'java'
apply plugin: 'maven'
dependencies {
    compile 'com.android.tools.lint:lint-api:24.5.0'
    compile 'com.android.tools.lint:lint-checks:24.5.0'
}
    
jar {
    manifest {
        attributes('Lint-Registry': 'com.netease.htlint.lintrules.MyIssueRegistry')
    }
}
    
configurations {
    lintJarOutput
}
    
dependencies {
    lintJarOutput files(jar)
}
    
defaultTasks 'assemble'

同时新建另一个工程 htlint,在其 build.gradle 文件中添加如下代码:

/*
 * rules for including "lint.jar" in aar
 */
configurations {
    lintJarImport
}
    
dependencies {
    lintJarImport project(path: ':htlintrules_jar', configuration: "lintJarOutput")
}
    
task copyLintJar(type: Copy) {
    from (configurations.lintJarImport) {
        rename {
            String fileName ->
                'lint.jar'
        }
    }
    into 'build/intermediates/lint/'
}
    
project.afterEvaluate {
    def compileLintTask = project.tasks.find { it.name == 'compileLint' }
    compileLintTask.dependsOn(copyLintJar)
}

最后在 app 工程的 build.gradle 中添加 htlint 引用,配置完成

dependencies {
    compile project(':htlint') // lint 检查库
    ...
}
4.3.3.7 自定义 Lint 检查执行及结果检查

${项目工程}/app/ 目录下执行 ../gradlew lint

image

根据提示查看 lint-result.html 文件,可以查看到前面编写的 ActivitySuperClassDetector.ACTIVITY_SUPER_CLASS_ISSUE 已经生效,并且检查出了相关的非规范代码。

image
image
4.3.3.8 排除错误的检查结果

前面很好的给出了检查结果了,然而我们会发现,FullScreenVideoActivity 确实是需要的错误检查结果,而 WXEntryActivity 却不是,这个类是有集成微信分享时需要的,并且按照微信开放平台的文档来编写,因此并不需要按照项目规范,继承 BaseBlankActivityBaseActionBarActivity。为此,我们期望 WXEntryActivity 不应该被检查出 WrongActivitySuperClass 错误

为此,我们可以在 WXEntryActivity 类名签名添加 SuppressLint 注解:

@SuppressLint("WrongActivitySuperClass")
public class WXEntryActivity extends Activity implements IWXAPIEventHandler{
    ...
}
  1. 排除 java 类或者方法的 Lint 检查

    若需要抑制某个 Issue 检查,可以在类定义签名或者方法定义签名,添加注解 @SuppressLint(${IssueId})。这里设置的就是具体某个 Issueid

    若需要抑制全部的 Issue 检查,可以使用 all 关键字,比如:@SuppressLint("all")

  2. 排除 xml 资源的 Lint 检查

    如项目中引入微博分享 sdk,按照官方文档,需要在 AndroidManifest 中声明 com.sina.weibo.sdk.net.DownloadService 这个 Service,而这个 Service 会被 Lint 检查为未定义,为此需要 xml 文件中也过滤部分代码的 Lint 的检查:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.netease.yanxuan">
        
        ...
        
        <service
            android:name="com.sina.weibo.sdk.net.DownloadService"
            android:exported="false"
            tools:ignore="MissingRegistered" />
    </manifest>
    

    这里对于单个 Issue 过滤的规则为:tools:ignore=${IssueId}

    如果需要过滤全部的 Issue,可以使用 all 关键字:tools:ignore="all"

4.4 其他代码检查工具

4.4.1 360 火线

360 火线 是 360 公司和信息安全部门深度合作,定制的适用于 360 公司产品的安卓 APP 安全检查规则。总共覆盖 61 项代码检查。使用也非常方便,细节看 使用文档,可以直接使用 jar 包并执行命令或集成 Android Studio Plugin 执行检查

image

4.4.2 pmd

pmd 代码检查工具,包含 16 个规则集,涵盖了 Java 的各种常见问题。其中规则集包含 基本(rulesets/basic.xml)终结函数(finalizer)未使用的代码(rulesets/unusedcode.xml)设计(rulesets/design.xml) 等。

相比 FindBugspmd 的一些规则更具争议,但 pmd 支持我们构建自己的规则集

<?xml version="1.0"?>
<ruleset name="customruleset">
  <description>
  Sample ruleset for developerWorks article
  </description>
  <rule ref="rulesets/design.xml"/>
  <rule ref="rulesets/naming.xml"/>
  <rule ref="rulesets/basic.xml"/>
</ruleset>

4.5 代码检查工具整合及集成 jenkins

为整合这些检查工具,在 gradle 中自定义 check 命名,并依赖其他的 task。在执行检查的时候,可以通过 ./gradlew check 来执行全部的检查命令。

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

另一方面,这种代码检查,如果等到开发完成的时候再去执行,很可能问题积累了很多,甚至导致产品上线前,开发并不能来得及修正全部的问题。为此,可以将代码检查的命令集成 jenkins,保证开发每天都能看到当前的代码的缺陷,能及时的修改

5 总结

我们从编码前的编码规范,编码进行中的编码模板,编码结束后的代码静态检查,保障了程序小伙伴们的代码。除此之外,还有很多不完善的地方需要我们做进一步处理:

  1. 和特定项目相关的自定义 Lint 检查项,仅支持了一部分的编码规范,自定义 Lint 检查项需要后续完成
  2. 虽然检查工具很多,检查的结果内容也很多,而很明显,这些检查工具相互之间是有重复的情况(主要是纯 java 代码部分的检查),因此如果整理检查结果,并过滤出我们真正关心的问题,也是后续需要完成的内容
Android
Web note ad 1