1.前言:
一个免费的 Android Studio插件,可以在开发早期检测出常见的 Java bug."我们通常都会在APP 上线之后,发现各种错误,尤其是空指针异常,这些错误对于用户体验来说是非常不好的,但其实大部分的问题,我们都能够提前发现。在编写代码的过程中,可能不会时时刻刻记得检查空的引用,还有删除没有用过的变量,在我们自己检测的过程中可能难以发现问题,一旦app上线,用户的时候用环境改变,这些潜在的问题就可能会冒出来”
2.FindBugs
顾名思义,FindBugs是一个寻找bug的工具,更具体的说FindBugs是一个静态检测java代码的工具,检测完成之后会生成一份详细的报告,借助这份报告可以找到潜在的Bug,比如前面说到的NullPointException,还可以检查特定的资源没有关闭,例如:查询数据库没有调用Cursor.close()等。
FindBugs官网地址:http://findbugs.sourceforge.net/
2.1作用
检测范围 :
常见代码错误,序列化错误
可能导致错误的代码,如空指针引用
国际化相关问题:如错误的字符串转换
可能受到的恶意攻击,如访问权限修饰符的定义等
多线程的正确性:如多线程编程时常见的同步,线程调度问题。
运行时性能问题:如由变量定义,方法调用导致的代码低效问题
2.2Android Studio gradle配置
在setting->plugin里边安装成功之后重启AS:
安装成功以后:会在logCat同级出现一个findBugs的图标,点击之后会有如下界面:
圈1.是点击执行的按钮;
圈2.是检测发现的问题目录,中间是错误目录或者错误所对应的类;
● Bad practice 坏的实践
一些不好的实践,下面列举几个: HE: 类定义了equals,却没有hashCode;或类定义了equals,却使用Object.hashCode;或类定义了hashCode,却没有equals;或类定义了hashCode,却使用Object.equals;类继承了equals,却使用Object.hashCode。 SQL:Statement 的execute方法调用了非常量的字符串;或Prepared Statement是由一个非常量的字符串产生。 DE: 方法终止或不处理异常,一般情况下,异常应该被处理或报告,或被方法抛出。 Malicious code vulnerability 可能受到的恶意攻击
如果代码公开,可能受到恶意攻击的代码,下面列举几个: FI: 一个类的finalize应该是protected,而不是public的。 MS:属性是可变的数组;属性是可变的Hashtable;属性应该是package protected的。
● Correctness 一般的正确性问题
可能导致错误的代码,下面列举几个:
NP: 空指针被引用;在方法的异常路径里,空指针被引用;方法没有检查参数是否null;null值产生并被引用;
null值产生并在方法的异常路径被引用;
传给方法一个声明为@NonNull的null参数;
方法的返回值声明为@NonNull实际是null。
Nm: 类定义了hashcode方法,但实际上并未覆盖父类Object的hashCode;类定义了tostring方法,但实际上并未覆盖父类Object的toString;很明显的方法和构造器混淆;方法名容易混淆。
SQL:方法尝试访问一个Prepared Statement的0索引;方法尝试访问一个ResultSet的0索引。
UwF:所有的write都把属性置成null,这样所有的读取都是null,这样这个属性是否有必要存在;或属性从没有被write。
● Dodgy 危险的
具有潜在危险的代码,可能运行期产生错误,下面列举几个:
CI: 类声明为final但声明了protected的属性。
DLS:对一个本地变量赋值,但却没有读取该本地变量;本地变量赋值成null,却没有读取该本地变量。
ICAST: 整型数字相乘结果转化为长整型数字,应该将整型先转化为长整型数字再相乘。
INT:没必要的整型数字比较,如X <= Integer.MAX_VALUE。 NP: 对readline的直接引用,而没有判断是否null;对方法调用的直接引用,而方法可能返回null。 REC:直接捕获Exception,而实际上可能是RuntimeException。 ST: 从实例方法里直接修改类变量,即static属性。
https://blog.csdn.net/natural_story/article/details/53260211这是关于findBugs的详细规则
● Performance 性能问题
可能导致性能不佳的代码,下面列举几个:
DM:方法调用了低效的Boolean的构造器,而应该用Boolean.valueOf(…);
用类似Integer.toString(1) 代替new Integer(1).toString;
方法调用了低效的float的构造器,应该用静态的valueOf方法。
SIC:如果一个内部类想在更广泛的地方被引用,它应该声明为static。
SS: 如果一个实例属性不被读取,考虑声明为static。
UrF:如果一个属性从没有被read,考虑从类中去掉。
UuF:如果一个属性从没有被使用,考虑从类中去掉。
● Multithreaded correctness 多线程的正确性多线程编程时,可能导致错误的代码,下面列举几个:
ESync:空的同步块,很难被正确使用。
MWN:错误使用notify,可能导致IllegalMonitorStateException异常;或错误的使用wait。
No: 使用notify而不是notifyAll,只是唤醒一个线程而不是所有等待的线程。
SC: 构造器调用了Thread.start,当该类被继承可能会导致错误。
● Internationalization 国际化 当对字符串使用upper或lowercase方法,如果是国际的字符串,可能会不恰当的转换
圈3.是错误的提示;
2.3FindBugs配置:
2.3.1在设置里找到FB的配置处有一些默认的配置
2.3.2配置规则(Gradle插件):
这里建议大家单独用gradle去设置检查规则
在app的build.gradle里添加一下方法
task findbugs(type: FindBugs,dependsOn:"assembleDebug") {
ignoreFailures =false
effort ="max"
reportLevel ="high"
excludeFilter =new File("${project.rootDir}/check_config/findbugs.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()
}
代码解释:
引入FindBugs的插件:apply plugin: "findbugs"。
定义一个task任务,这个任务的类型是FindBugs,指定依赖assembleDebug是为了先生成.classe文件,才能对代码进行静态分析。
ignoreFailures:有警告错误的时候也是允许构建。
reportLevel:报告的级别,默认是medium ,Low,Medium,High一般来说我们首先关注的是高级别的报告,再关注低一级别的报告。
classes和source分别是对应的.classe文件夹地址,和源代码文件地址。
repoets指定报告类型,有两种方式xml和html,只允许一种输出格式。
effort 可以设置成max和min,一般在内存和时间不紧张的情况下都用max
定义完成之后,同步下Gradle,之后在右侧的Gradle的菜单中找到对于的Module,就可以在Tasks中找到对应的findBugs任务,点击即可运行。
2.4 findbugs-filter
excludeFilter就是用来配置过滤器的,过滤内容则通过findbugs-filter.xml文件来约定,也是findbugs配置中最重要的一个环节。
findbugs的检查报告包括了分好几大类型的警告(Warning Type),如图中的Bad prictice Warnings,Internationalization Warnings等类型,每个类型下又有其他的代码警告(Code Warning),如图中 的code码Nm和后面的介绍,当然其中有很多我们不关心的警告Code,比如Eq(建议用equals()来代替==进行比较的警告)UrF(提示Activity中的控件如TextView没有初始化,其实我们是通过注入初始化的)等在某些类型的警告中是我们不关注的,需要过滤掉。接下来简单说一下如何配置过滤规则。
过滤标签介绍
<Bug>设置警告的类型
<Confidence>设置过滤等级
<Package>设置过滤包名
<Class>设置过滤的类名
<Source>设置过滤的源文件名
<Method>设置过滤的方法名
过滤组合操作符:
<Or>或
<And>与
<Not>非
这里把国际化相关的警告全部过滤掉了
<Match>
<Bug code = "IJU"/>
</Match>
有的时候我们可能只想过滤掉某个大的类别中的几个小的类型的警告,比如过滤掉Performence中的UrF
<Match>
<And>
<Bug category="PERFORMANCE"/>
<Bug code= "SIC,UrF,UuF"/>
</And>
</Match>
过滤某个特定的类:
<Match>
<Class name = "com.foobar.MyClass"/>
</Match>
运用组合条件过滤某个类中的特定内容警告:
<Match>
<Class name = "com.foobar.MyClass"/>
<Or>
<Method name = "frob" category="int,java.lang.String" returns = "void">
</Method>
<Method name = "blat" category=" " returns = "boolean"/>
</Or>
</Match>
当然用法可以很灵活,能够非常方便的定制符合业务需求的过滤器 ,从而达到最高效解决我们关注问题的目的。
2.5FindBugs报告
在 logcat同级的操作中可以指定目录,在gradle project中点击findbugs执行会在
build\reports\findbugs\下边生成报告,报告的格式根据上文提到的app.gradle配置决定
单个文件执行结果:
生成报告如下:
小记:
这个插件是基于Java的结果,现在android如果使用kotlin语言,那么就要考虑使用性。后期会为大家分享基于kotlin的一些检测工具