Android项目中添加代码检查

简介

Android开发项目中需要我们将我们的代码按照一定的规则进行格式化,减少在编写代码是的一些错误行为,我们选择了以下的两种方式来进行规避

1.checkstyle

checkstyle,是一个规范Java编码行为的插件,可以自定义规范,我们可以在Android Studio上配置使用。

1.1 配置

1.1.1 首先我们可以在当前项目中自己写一个checkstyle.gradle文件

apply plugin: 'checkstyle'

//设置CheckStyle版本
checkstyle {
  toolVersion '8.11'
}

//设置配置文件
task checkstyle(type: Checkstyle) {
  description 'Check code standard'
  group 'verification'

  //配置过滤规则的文件地址
  configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/checkstyle/suppressions.xml").absolutePath
  //检查规则的文件地址
  configFile file("${project.rootDir}/checkstyle/checkstyle.xml")
  source 'src'
  include '**/*.java'
  exclude '**/gen/**'
  exclude '**/test/**'
  exclude '**/androidTest/**'

  classpath = files()
}

1.1.2 我们再新建checksyle.xml文件,以下是当前我设置的规则,也可以使用谷歌提供的google_chesks,基于此灵活修改即可

<?xml version="1.0"?><!DOCTYPE module PUBLIC
  "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
  "https://checkstyle.org/dtds/configuration_1_3.dtd">

<!--
    Checkstyle configuration that checks the Google coding conventions from Google Java Style
    that can be found at https://google.github.io/styleguide/javaguide.html.

    Checkstyle is very configurable. Be sure to read the documentation at
    http://checkstyle.sf.net (or in your downloaded distribution).

    To completely disable a check, just comment it out or delete it from the file.

    Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
 -->

<module name="Checker">
  <property name="charset" value="UTF-8" />

  <property name="fileExtensions" value="java, properties, xml" />
  <module name="SuppressWarningsFilter" />
  <module name="TreeWalker">
    <module name="SuppressWarnings">
      <property name="id" value="checkstyle:suppresswarnings" />
    </module>
    <module name="SuppressWarningsHolder" />
    <module name="OuterTypeFilename" />
    <module name="IllegalTokenText">
      <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL" />
      <property name="format"
        value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)" />
      <property name="message"
        value="Consider using special escape sequence instead of octal value or Unicode escaped value." />
    </module>
    <module name="AvoidEscapedUnicodeCharacters">
      <property name="allowEscapesForControlCharacters" value="true" />
      <property name="allowByTailComment" value="true" />
      <property name="allowNonPrintableEscapes" value="true" />
    </module>
    <module name="LineLength">
      <property name="max" value="130" />
      <property name="ignorePattern"
        value="^package.*|^import.*|a href|href|http://|https://|ftp://" />
    </module>
    <module name="AvoidStarImport" />
    <module name="OneTopLevelClass" />
    <module name="NoLineWrap" />
    <module name="EmptyBlock">
      <property name="option" value="TEXT" />
      <property name="tokens"
        value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH" />
    </module>
    <module name="NeedBraces" />
    <module name="LeftCurly" />
    <module name="RightCurly">
      <property name="id" value="RightCurlySame" />
      <property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
                    LITERAL_DO" />
    </module>
    <module name="RightCurly">
      <property name="id" value="RightCurlyAlone" />
      <property name="option" value="alone" />
      <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
                    INSTANCE_INIT" />
    </module>
    <module name="WhitespaceAround">
      <property name="allowEmptyConstructors" value="true" />
      <property name="allowEmptyMethods" value="true" />
      <property name="allowEmptyTypes" value="true" />
      <property name="allowEmptyLoops" value="true" />
      <!--<message key="ws.notFollowed"
        value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
      <message key="ws.notPreceded"
        value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>-->
    </module>
    <module name="OneStatementPerLine" />
    <module name="ArrayTypeStyle" />
    <module name="MissingSwitchDefault" />
    <module name="FallThrough" />
    <module name="SeparatorWrap">
      <property name="id" value="SeparatorWrapDot" />
      <property name="tokens" value="DOT" />
      <property name="option" value="nl" />
    </module>
    <module name="SeparatorWrap">
      <property name="id" value="SeparatorWrapComma" />
      <property name="tokens" value="COMMA" />
      <property name="option" value="EOL" />
    </module>
    <module name="SeparatorWrap">
      <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
      <property name="id" value="SeparatorWrapEllipsis" />
      <property name="tokens" value="ELLIPSIS" />
      <property name="option" value="EOL" />
    </module>
    <module name="SeparatorWrap">
      <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
      <property name="id" value="SeparatorWrapArrayDeclarator" />
      <property name="tokens" value="ARRAY_DECLARATOR" />
      <property name="option" value="EOL" />
    </module>
    <module name="SeparatorWrap">
      <property name="id" value="SeparatorWrapMethodRef" />
      <property name="tokens" value="METHOD_REF" />
      <property name="option" value="nl" />
    </module>
    <module name="PackageName">
      <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
      <message key="name.invalidPattern" value="Package name ''{0}'' must match pattern ''{1}''." />
    </module>
    <module name="TypeName">
      <message key="name.invalidPattern" value="Type name ''{0}'' must match pattern ''{1}''." />
    </module>
    <!--<module name="LambdaParameterName">
      <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
      <message key="name.invalidPattern"
        value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
    </module>-->
    <module name="CatchParameterName">
      <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$" />
      <message key="name.invalidPattern"
        value="Catch parameter name ''{0}'' must match pattern ''{1}''." />
    </module>
    <module name="ClassTypeParameterName">
      <property name="format" value="^([0-9a-zA-Z])+$" />
      <message key="name.invalidPattern"
        value="Class type name ''{0}'' must match pattern ''{1}''." />
    </module>
    <module name="MethodTypeParameterName">
      <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
      <message key="name.invalidPattern"
        value="Method type name ''{0}'' must match pattern ''{1}''." />
    </module>
    <module name="InterfaceTypeParameterName">
      <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
      <message key="name.invalidPattern"
        value="Interface type name ''{0}'' must match pattern ''{1}''." />
    </module>
    <module name="NoFinalizer" />
    <module name="GenericWhitespace">
      <message key="ws.followed" value="GenericWhitespace ''{0}'' is followed by whitespace." />
      <message key="ws.preceded" value="GenericWhitespace ''{0}'' is preceded with whitespace." />
      <message key="ws.illegalFollow"
        value="GenericWhitespace ''{0}'' should followed by whitespace." />
      <message key="ws.notPreceded"
        value="GenericWhitespace ''{0}'' is not preceded with whitespace." />
    </module>
    <module name="Indentation">
      <property name="basicOffset" value="2" />
      <property name="braceAdjustment" value="0" />
      <property name="caseIndent" value="2" />
      <property name="throwsIndent" value="2" />
      <property name="lineWrappingIndentation" value="4" />
      <property name="arrayInitIndent" value="4" />
    </module>
    <module name="AbbreviationAsWordInName">
      <property name="ignoreFinal" value="false" />
      <property name="allowedAbbreviationLength" value="10" />
    </module>
    <module name="OverloadMethodsDeclarationOrder" />
    <module name="MethodParamPad" />
    <module name="NoWhitespaceBefore">
      <property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF" />
      <property name="allowLineBreaks" value="true" />
    </module>
    <module name="ParenPad" />
    <!--<module name="OperatorWrap">-->
    <!--<property name="option" value="NL"/>-->
    <!--<property name="tokens"-->
    <!--value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,-->
    <!--LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>-->
    <!--</module>-->

    <module name="SuppressionCommentFilter" />
    <module name="AnnotationLocation">
      <property name="id" value="AnnotationLocationMostCases" />
      <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF" />
    </module>
    <module name="AnnotationLocation">
      <property name="id" value="AnnotationLocationVariables" />
      <property name="tokens" value="VARIABLE_DEF" />
      <property name="allowSamelineMultipleAnnotations" value="true" />
    </module>
    <module name="MethodName">
      <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$" />
      <message key="name.invalidPattern" value="Method name ''{0}'' must match pattern ''{1}''." />
    </module>
    <module name="CommentsIndentation" />
  </module>
</module>

如果我们有部分文件不想要被此规则来检查,我们可以设置过滤规则在suppressions.xml文件中

<?xml version="1.0"?>

<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
  "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
  <suppress checks="[a-zA-Z0-9]*" files="R.java" />
  <suppress checks="[a-zA-Z0-9]*" files="BuildConfig.java" />
</suppressions>

有需要的可以自己添加就可以了。

我们还需要在当前项目的build.gradle文件中使用此插件

allprojects {
  //当前项目的代码检查规范,xxx为checkstyle.gradle的路径地址
  apply from: file("${project.rootDir}/xxx"
}

这样我们项目里的代码就可以使用上述规范来进行检测了。
我们使用命令行来执行

./gradlew checkstyle

如果有错误命令行会提示,进行相应的修改即可。

1.2 checkstyle插件使用

如果只是在命令行使用,按照上述的配置就可以完成了,但是我们需要的是在编写代码时候实时的提示,这样我们就需要下载一个插件

checkstyle插件

然后在Preferences中的Tools中进行配置,配置的文件地址为上面已经编辑好的checkstyle.xml的文件地址,选择即可,这里面由于版本问题可能会导致导入进来的失败,我们需要配置checkstyle version的版本即可,我在项目里配置的插件版本是8.5

1.3 code style配置

项目中的code style是由其他人来配置的,我们需要在Preferencescode style中导入新的文件,我们再写代码时会进行代码格式化等操作,都是按照code style的规则来做的,我们可以将checkstyle.xml的规则配置进行修改同code style中的规则保持一致。按照以上的步骤那么checkstyle就配置完成了!

1.4 检查策略

我们配置好了checkstyle,但是不是强制的,即使不遵守也是没关系的,这样按照项目需求我配置了在代码提交前进行检查,这样没有通过的话是不能进行提交的!
我们可以修改.git/hooks/pre-commit文件,修改为:

#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

if git rev-parse --verify HEAD >/dev/null 2>&1
then
	against=HEAD
else
	# Initial commit: diff against an empty tree object
	against=$(git hash-object -t tree /dev/null)
fi

SCRIPT_DIR=$(dirname "$0")
SCRIPT_ABS_PATH=`cd "$SCRIPT_DIR"; pwd`
$SCRIPT_ABS_PATH/../../gradlew checkStyle
if [ $? -eq 0   ]; then
    echo "checkstyle OK"
else
    exit 1
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
	# Note that the use of brackets around a tr range is ok here, (it's
	# even required, for portability to Solaris 10's /usr/bin/tr), since
	# the square bracket bytes happen to fall in the designated range.
	test $(git diff --cached --name-only --diff-filter=A -z $against |
	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
	cat <<\EOF
Error: Attempt to add a non-ASCII file name.

This can cause problems if you want to work with people on other platforms.

To be portable it is advisable to rename the file.

If you know what you are doing you can disable this check using:

  git config hooks.allownonascii true
EOF
	exit 1
fi

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

这样整个checkstyle就配置完成了!

2.Android Lint

Android Lint是自带的检查规范,我们需要先在gradle中配置,可以在模块级别的build.gradle中进行设置

lintOptions {
    lintConfig file("$project.rootDir/lint/lint.xml")
    baseline file("$project.rootDir/lint/$project.name-lint-baseline.xml")
    warningsAsErrors true
    abortOnError true
    htmlReport true
    htmlOutput file("$project.rootDir/reports/lint/$project.name-lint.html")
    xmlReport false
  }

lintConfig中的设置就是忽略检查的部分,baseline只有在第一次设置时有效,如果我们之前的代码问题较多,但是已经上线了,改动比较大,不想修改之前的代码,我们可以配置这一属性,AS会自动输出文件到指定的文件路径地址,这样我们可以检测我们后续新增的代码。

<?xml version="1.0" encoding="UTF-8"?>
<!--lint.xml文件-->
<lint>
  <!-- list of issues to configure -->
  <issue id="Overdraw" severity="ignore"/>
  <issue id="GradleDependency" severity="ignore"/>
  <issue id="StaticFieldLeak" severity="ignore"/>
</lint>

这样我们就配置完成了,可以使用命令行执行:

./gradlew lint`在这里插入代码片`

因为这一步耗时还是比较久的,可以在代码提交完成的pipeline中检查,如果检查通过才可以进行merge这样可以控制代码的质量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值