简介
在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
插件使用
如果只是在命令行使用,按照上述的配置就可以完成了,但是我们需要的是在编写代码时候实时的提示,这样我们就需要下载一个插件
然后在Preferences
中的Tools
中进行配置,配置的文件地址为上面已经编辑好的checkstyle.xml
的文件地址,选择即可,这里面由于版本问题可能会导致导入进来的失败,我们需要配置checkstyle version
的版本即可,我在项目里配置的插件版本是8.5
。
1.3 code style
配置
项目中的code style
是由其他人来配置的,我们需要在Preferences
的code 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
这样可以控制代码的质量。