AndroidQualityEssentials [English Version]
通过静态代码分析和运行时的检查来提高安卓代码质量:
- 命名规范(特别是资源文件的命名):CheckStyle
- 代码风格:CheckStyle
- 潜在的缺陷:FindBugs、PMD以及Android Lint
- 潜在的ANR (由主线程中的耗时操作引起的Android Not Responding):StrictMode
- 资源和内存的泄露:StrictMode和LeakCanary
最好的使用方式,是在创建新项目的时候就引入这些规则,在每次check-in之前修复检查到的任何错误(作为持续集成的一个检查步骤)。否则的话,面对成百上千的错误,是需要很大的勇气和毅力去逐个修复的。『从入门到放弃』并不是解决代码质量问题的正确态度。
- 把quality目录加入你的项目。
- 可以直接拷贝:
- 同步项目代码,拷贝quality目录到你项目的根目录下。
- 在项目的
build.gradle
开头加入下面这一行:
apply from: '../quality/static_analysis.gradle'
- 或者作为remote module加入你的项目。就像在我的项目Android-App-Architecture-MVVM-Databinding里面这样:
- 加入remote module:
git remote add analysis https://github.com/BrianSpace/Android-Quality-Essentials.git
- 然后加入
build.gradle
:
apply from: '../analysis/quality/static_analysis.gradle'
- 可以直接拷贝:
- 加入Lint配置
android {
...
lintOptions {
// Turn off analysis progress reporting by lint
quiet true
// Stop the gradle build if errors are found
abortOnError true
// Do not ignore lint warnings
ignoreWarnings false
// Treat warnings as errors
warningsAsErrors true
// Ignore rules list
ignore 'GoogleAppIndexingWarning' // Remove this if the app support App Indexing
}
...
}
- 在
build.gradle
中引入LeakCanary的依赖:
dependencies {
...
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
...
}
- 在安卓应用的
Application
类的onCreate中加入下列代码(别忘了加入AndroidManifest.xml中):
public class AndroidQualityEssentialsApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyDeath() // If violations are in Android itself or 3rd-party libs, use penaltyLog.
.build());
// Avoid the process dedicated to LeakCanary for heap analysis.
if (!LeakCanary.isInAnalyzerProcess(this)) {
LeakCanary.install(this);
}
}
...
}
}
- 在命令行中运行
gradlew check
来进行静态代码检查。
- 分析报告在应用了
static_analysis.gradle
的项目的build/reports/
目录下。
- 运行Debug版本的应用进行运行时检查。
- 把check过程加入持续集成的步骤中。
在命名规则中不允许像"mMember"这样以单个小写字母为前缀的成员变量命名方式。但是如果你喜欢这种命名方式,可以把"MemberName
"模块的"format
"属性改成"^[a-z][a-zA-Z0-9]*$
"。也可以改成"^m[A-Z][a-zA-Z0-9]*$
"来强制使用以"m"为前缀的成员变量命名方式。
- 在Android Studio中打开"Analyze"->"Run Inspection by Name..."菜单,搜索"be final",然后运行在"Java"->"Code style issues"下的以下两条规则:
- Field may be 'final'
- Local variable or parameter may be final
- 每次检查完成后,在结果面板中点击"Make final"按钮,就可以自动添加"final"关键字了。
对于PMD规则UseUtilityClass,如果你的类只有静态成员变量和函数,会提示你创建Utility Class。你需要:
- 把类定义为final。
- 创建一个私有的构造函数并抛出异常,以免被实例化。 例子:
public final class FileUtil {
private static Context appContext;
private FileUtil() throws InstantiationException {
throw new InstantiationException("Utility class FileUtil should not be instantiated!");
}
public static void init(final Context context) {
appContext = context.getApplicationContext();
}
/**
* Get available cache directory. Prefer external over internal.
*/
@NonNull
public static File getAvailableCacheDir() {
final File externalCacheDir = appContext.getExternalCacheDir();
return externalCacheDir == null ? appContext.getCacheDir() : externalCacheDir;
}
}
代码检查的规则都是人们长期总结出来的最佳实践,但并不是放之四海而皆准的真理。有些规则需要根据项目的具体需求来确定是否采用:
- AccessorMethodGeneration 这个规则更关注性能以及减少方法数,单就我个人而言更关心信息的封装,所以我在PMD配置中排除了这个规则。如果你更关心运行的性能以及方法数(以避免Multi-dex的问题),那你就应该把这个规则包含进来。
- GenericsNaming
这个规则要求泛型的参数都采用单个大写字母。从可读性的角度我更喜欢更有意义的命名方式:以"T"结尾的类型命名,如
ItemTypeT
。如果你更喜欢看起来更简单的单字母命名,可以把这个规则从exclude中去掉。
命名规范定义在quality/checkstyle/naming_convention.xml文件中。
- Java文件:
- 使用驼峰(Camel)命名法
- 测试文件须命名为"Test.java"或者"Base.java"
- 资源文件:
- 使用蛇形命名法(全小写,以下划线分隔)。
- Drawables以"bg_"、"ic_"或者"img_"开头。
- Layouts布局文件以"activity_"、"fragment_"、"view_"、"dialog_"、"item_"或"btn_"开头。
- Values以"attrs_"、"colors_"、"dimens_"、"strings_"或"styles_"开头。
你可以根据项目的命名规范修改相关的正则表达式。
如果只想检查命名规范,可以运行gradlew checkFileNames
。
CheckStyle被用来检查Java代码风格。代码风格定义基于Google Java Style Guide,并做了如下修改:
- Severity: 改为"error"
- Line length: 改为120
- Indentation: tab大小改为4
- MethodName: 不允许使用下划线
- ConstantName: 全大写,允许下划线
如果只想检查代码风格,可以运行gradlew checkCodeStyle
。
如果需要在检查时排除一些文件(比如来自第三方的代码),可以在static_analysis.gradle中的checkCodeStyle
任务内增加exclude
项目。
FindBugs检查代码中可能会导致Bug的模式。检查中需要排除在外的文件定义在这儿.
如果只想进行findbugs检查,可以运行gradlew findBugs
。
PMD用来检查代码中常见缺陷的工具。规则定义在quality/pmd/pmd-ruleset.xml中。规则的完整列表参见这儿.
如果只想进行PMD检查,可以运行gradlew pmdCheck
。
Android Lint为安卓特别开发的代码检查工具。完整的检查列表参考这儿.
如果只想进行Lint检查,可以运行gradlew lint
。
StrictMode对发现可能会导致UI无响应操作以及资源泄露很有效。 ThreadPolicy用来检查UI线程中的磁盘、网络I/O以及耗时操作;VmPolicy则用来检查未释放的资源。 详情请参考ThreadPolicy.Builder 以及VmPolicy.Builder。
LeakCanary能帮助你发现内存泄露。它有自己单独的UI用来汇报内存泄露,列出的引用链让你能很容易地找到该从哪里断开链条,修复泄露。
- 使用findbugs和PMD的部分参考了https://github.com/ribot/android-boilerplate.
The MIT License
Copyright (c) 2017-2017 AndroidQualityEssentials project contributors
https://github.com/BrianSpace/AndroidQualityEssentials/graphs/contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.