Android Lint扫描规则说明(二)

主要内容

对Android Studio支持的六类Android Lint规则, 本文主要对Performance包含的32个项的说明,主要内容都是文档翻译,适当加一些自己的感想。

分类详细说明

高效使用资源

UnusedIds

未被使用的资源id,在layout文件中定义了资源ID从未被使用过,但有时候它们可以让layout更容易阅读,没有必要删除未使用的资源id。

Overdraw

过度绘制:一个绘制区域被绘制的次数多于一次。

如果给一个root view设置了背景图,就要给它设置一个background=nulltheme,否则绘制过程会先绘制themebackground,然后再绘制设置的背景图,完全覆盖之前绘制的theme.background,这是 过度绘制

这个检测器依赖于根据扫描Java代码找出哪些布局文件与哪些Activity相关联,目前它使用的是一种不精确的模式匹配算法。因此,可能会因错误地推断布局与活动的关联而给出错误的提醒。

如果想把一个背景图应用在多个页面上,可以考虑自定义theme,并把背景图设置在theme里,在layout中设置theme代替设置background。如果背景图中有透明的部分,并且希望他和theme的背景有层叠效果,那么可以选择先把两个背景合并成一个背景图之后,在定义到theme里。

VectorPath

关于SVG的使用,给出一篇参考文章:Android vector标签 PathData 画图超详解,Android Studio可以创建使用SVG绘制出的drawable图像资源。

UselessLeaf

没有包含任何View,也没有设置背景的Layout是多余的,可以去掉。让界面更趋于扁平,嵌套更高效。

UselessParent

如果一个包含ViewLayout没有兄弟层级的Layout,而他的外部ViewGroup又不是ScrollView或者root级别,那么这个Layout可以移除,让他包含的View直接包含在它的父层级的Layout中。让界面更趋于扁平,嵌套更高效。

TooDeepLayout

Layout嵌套过深会影响性能,考虑使用平铺类型的Layout代替。默认最深的View嵌套是10层,也可以通过环境变量ANDROID_LINT_MAX_DEPTH进行设置。System.getenv("ANDROID_LINT_MAX_DEPTH");语句获取,如何设置还没找到。

TooManyViews

Layout内有太多的View:一个Layout文件内有过多的View会影响性能。考虑使用复合drawables或其他技巧来减少这个布局中的视图数量。默认最多的数量是80个,可以通过环境变量ANDROID_LINT_MAX_VIEW_COUNT进行设置。据说这个变量可以用System.getenv("ANDROID_LINT_MAX_DEPTH");语句获取,如何设置还没找到。

NestedWeights

Weight嵌套:使用非0layout-weight值,需要Layout被测量两次,如果一个包含非0值的LinearLayout被嵌套在另一个包含非0值的LinearLayout内部,那么,测量次数就会呈指数级增长。

DuplicateDivider

这个主要是讲RecyclerView的分割线,com.android.support:recyclerview-v7 提供了一个类DividerItemDecoration设置分割线样式,这个类在早期的版本内没有包含,所以在更新为新的版本后,可以使用这个类重新设置分割线。
具体使用,参考文章:Android RecyclerView 使用完全解析

MergeRootFrame

FrameLayout在一个layout文件中是root且没有使用background或者padding等属性,通常使用一个merge标签代替FrameLayout会更高效。但是这要看上下文设置,所以在替换之前要确认你已经理解了merge标签的工作原理

UnusedResources

未使用的资源:多指的是drawable类型的资源。多余的drawable资源会让APP变大,编译过程变长。

InefficientWeight

当LinearLayout只有一个Widget且使用了android:layout_weight时,定义对应的width/height的值为0dp,Widget就会自动占满剩余空间。因为不需要预先计算自己的尺寸,这种方式更高效。

高效的设置

DisableBaselineAlignment

在使用LinearLayout实现空间的按比例分割时,LinearLayout的空间用layout_weight属性在所包含的几个layout中间分割,那么应该设置被分割LinearLayoutbaseLineAligned="false",这样可以加快分割空间所做的运算。

LogConditional

LogConditional:使用android.util.Log打印调试日志,一般只会在DEBUG模式下使用,在release是不需要打印调试日志的,在buildToolsVersion大于等于17时, BuildConfig提供两个一个DEBUG常量来标记是否处于DEBUG模式,我们可以用if(BuildConfig.DEBUG){}包裹调试日志语句,这样编译器会在编译生成release包时,删除这些语句。如果真的需要在release模式下打印调试日志,可以使用@SuppressLint("LogConditional")注解告诉编译器在release包中保留这些日志信息。

UnpackedNativeCode

APP使用System.loadLibrary()加载Native库时,android 6.0或者更新的版本可以在Manifest文件中application标签中添加属性android:extractNativeLibs="false",这样可以提交加载速度,降低APP占用的存储空间。

更高效的替代方案

FloatMath

不要使用FloatMath类进行数学计算,推荐使用Math类。

Android早期版本因为浮点运算性能的原因,推荐使用FloatMath代替Math类进行数学计算。随着硬件和系统的发展,这个问题已经不复存在,甚至经过JIT优化之后的Math类运算速度会比FloatMath更快,所以,在Android F以上版本的系统上,可以直接使用Math类,而不是FloatMath。

UseValueOf

某些类构造新对象时,建议使用工厂方法,而不是new关键字声明新的对象。例如,new Intger(0)就可以使用Integer.valueOf(0)替代,工厂方法会使用更少的内存,因为它会让值相等的对象使用同一个实例。

ViewHolder

在给ListViewGradView之类的列表实现Adapter时,不能每次getView调用都去inflate一个新的layout,如果接口参数中给出了一个可以复用的View对象,就可以使用这个对象而不是重新生成。这个应该都很熟悉,也很简单基础了。

UseSparseArrays

KeyInteger类型的HashMap可以使用SparseArray代替,性能更好。可以使用替代HashMap的有SparseBooleanArray、SparseIntArray、SparseLongArray和泛型类SparseArray,每个对应的类型代表Value的类型。如果在某些情况一定要用HashMap实现,则可以用@SuppressLint注解抑制Lint检查。

WakelockTimeout

关于week lock的使用,这里提供一篇博客文章:Android 功耗分析之wakelock

UseCompoundDrawables

在一个TextView的四周有只具有展示作用的ImageView时,建议删除ImageView改用compound drawables:drawableTop, drawableLeft, drawableRight,drawableBottom,drawablePadding替代方案实现。

有关泄漏的提醒

Recycle

缺少recycle()调用:许多资源例如:TypedArrays, VelocityTrackers在使用完之后需要调用recycle()方法回收资源。

ViewTag

4.0版本系统之前,View.setTag(int, Object)的实现方式中,会把Object存储在一个静态的map里并且使用的是强引用。这就意味着如果这个Object包含了对Context对象的引用,这个Context就是泄漏了。

传递一个View做参数,这个View就能提供一个对创建它的Context的引用。类似的,View holders内包含View,也会有Context与这个View相关联。

HandlerLeak

Handler引用泄漏:声明Handler的子类如MyHandler为内部类,如果MyHandler类对象关联Looper.getMainLooper()或者Looper.getMainLooper().getQueue()时,会阻止无用的外部类对象被垃圾回收,导致泄漏。如果对应main thread 的关联,就不会有这个问题。

应对方法,声明MyHandler为静态内部类,并用WeakReference的方式持有一个外部类对象,MyHandler使用这个对象操作外部类的属性和方法。

DrawAllocation

绘制过程中的内存分配:避免在布局绘制过程中分配内存给新的对象。因为这些操作调用频率比较高,频繁分配内存会唤起垃圾回收,中断UI绘制,导致卡顿。

StaticFieldLeak

非静态内部类具有对其外部类对象的隐式引用。

如果外部类Fragment或者Activity,那么这个引用意味着长时间运行的处理程序/加载器/任务(handler/loader/task)将持外部类对象的引用,从而防止外部类对象被回收。

同理,长时间运行的处理程序/加载器/任务(handler/loader/task)对Fragment或者Activity的直接引用,也会造成泄漏。

ViewModel类应该禁止引用View或者non-application类型的Context对象。

代码提醒

AnimatorKeep

属性动画默认支持的属性如下面列表。如果超出这些范围,会通过反射调用本地定义的函数。声明一个属性动画对象例如:ObjectAnimator.ofFloat(view, "rotation", 0, 360) 中的“rotation”就是要操作的属性,如果属性不在下面的列表中例如ObjectAnimator.ofFloat(view, "position", 0, 360),就需要本地定义一个对应的方法setPosition(float position),并且这个方法需要加上@keep注解,防止被当做无用方法清理掉。

    static {
        PROXY_PROPERTIES.put("alpha", PreHoneycombCompat.ALPHA);
        PROXY_PROPERTIES.put("pivotX", PreHoneycombCompat.PIVOT_X);
        PROXY_PROPERTIES.put("pivotY", PreHoneycombCompat.PIVOT_Y);
        PROXY_PROPERTIES.put("translationX", PreHoneycombCompat.TRANSLATION_X);
        PROXY_PROPERTIES.put("translationY", PreHoneycombCompat.TRANSLATION_Y);
        PROXY_PROPERTIES.put("rotation", PreHoneycombCompat.ROTATION);
        PROXY_PROPERTIES.put("rotationX", PreHoneycombCompat.ROTATION_X);
        PROXY_PROPERTIES.put("rotationY", PreHoneycombCompat.ROTATION_Y);
        PROXY_PROPERTIES.put("scaleX", PreHoneycombCompat.SCALE_X);
        PROXY_PROPERTIES.put("scaleY", PreHoneycombCompat.SCALE_Y);
        PROXY_PROPERTIES.put("scrollX", PreHoneycombCompat.SCROLL_X);
        PROXY_PROPERTIES.put("scrollY", PreHoneycombCompat.SCROLL_Y);
        PROXY_PROPERTIES.put("x", PreHoneycombCompat.X);
        PROXY_PROPERTIES.put("y", PreHoneycombCompat.Y);
    }
ObsoleteSdkInt

无用的SDK版本检查:Android SDK的版本更新比较快,许多API的使用都需要通过检查SDK版本防止出现not found之类的崩溃。在APP迭代的过程中提升了minSdkVersion的值就会导致部分SDK版本检查不再需要。

这种SDK版本检查会引起不必要的资源搜索。

DevModeObsolete

以前,文档中建议在productFlavors中创建一个dev product。设定minSdkVersion 21,在开发过程中激活multidexing加速构建过程。现在已经不需要这么做了,在新版的IDE和Gradle插件中,会自动地识别所连接设备的API level,如果链接的设备API level大于等于21,就会自动打开multindexing,就跟之前设置了dev product的效果一样。

参考:Enable Android MultiDex

ObsoleteLayoutParam

无用的LayoutParam:当给Widget使用了所在Layout没有提供的LayouParam时,会有这个提示。这种情况一般出现在修改Layout类型时没有同时修改内部Widget的LayoutParam设置或者把一个Widget从一个Layout拷贝到另一个不同类型的Layout内部。

这种无用的LayoutParam在运行时会引起无效的属性解析,也会误导阅读这些代码的人。所以应该把这些无用的属性删除掉。

其他

WearableBindListener
UseOfBundledGooglePlayServices

Android Lint扫描规则说明(一)

主要内容

对Android Studio支持的六类Android Lint规则, 本文主要对AccessibilityInternationalization 两中类型所包含的14个项的说明,主要内容都是文档翻译,适当加一些自己的感想。

Accessibility

可访问性的检查,除了第一项之外,其他项更像是为某些自动化的工具做的准备工作,不影响APP的运行。

ClickableViewAccessibility

可点击View的可访问性:如果重写onTouchEvent或使用OnTouchListener的View在检测到单击时没有实现performClick并调用它,则视图可能无法正确处理可访问性操作。理想情况下,处理单击操作的逻辑应该放在View#performClick中,因为当单击操作发生时,一些可访问性服务会调用performClick。

ContentDescription

非文本Widget描述:
– 首先,像ImageViews和ImageButtons这样的非文本Widget应该使用contentDescription属性指定文本对Widget进行说明,以便屏幕阅读器和其他可访问性工具能够充分描述和理解用户界面。
– 其次,如果一个非文本Widget在用户界面上只是一个装饰,不展示任何内容也不接受任何用户操作,就不需要给它提供描述性的contentDescription属性文本,而是使用tool属性ContentDescription抑制lint提醒。
– 第三,对于文本型Widget不能同时设置hintcontentDescription,否则hint将不会展示在界面上,只设置hint就可以。

参考:Make apps more accessible

GetContentDescriptionOverride

重写非文本Widget描述方法getContentDescription:重写View的getContentDescription方法,可能会阻止某些可访问性服务正确导航视图公开的内容。相反,当内容描述需要更改时,调用setContentDescription。

KeyboardInaccessibleWidget

键盘无法访问Widget:如果一个Widget声明了可以点击,但是没有声明可以获得焦点,这个Widget是无法通过键盘访问的,需要设置focusable=true。

LabelFor

缺少可访问标签:可编辑的控件例如EditText应该为hint属性赋值,或者在minSDKVersion大于等于17时,使用labelFor属性为EditText指定一个标签控件。标签控件可以是指定了text属性的文字控件如TextView,也可以是指定了contentDescription的非文字控件如ImageView。

如果被指定的标签控件如TextView在另外一个layout文件中,且使用layout属性引用了EditText所在的layout文件,可以ignore这个lint检查。

Internationalization

ByteOrderMark

查了一下这个ByteOrderMark,简称BOM,指的是一些标记字符。这种问题一般是“在不同编码格式的文件之间拷贝字符或者在某些文件系统上编辑了文件”导致的。

文件内BOM提醒:Lint会把文件中包含的BOM字符标记出来。因为Android工程中我们期望使用utf-8对文件和字符进行编码。BOM字符对utf-8来说不是必需的,而且有一些工具是不能正确处理带BOM字符的文本的。

参考:Android提示BOM错误排查UTF8最好不要带BOM

EnforceUTF8

资源文件编码格式非utf-8:XML文件对编码类型的支持比较广泛。然而有些工具不能正确某些类型编码的文件,而utf-8在Android应用中是一种被广泛支持的编码类型。使用utf-8对文件进行编码,可以防止在处理non-ASCII类型的字符时出现奇怪的问题。尤其是Gradle在合并XML类型的资源文件时,预先假定文件是使用utf-8进行编码的。

HardcodedText

硬编码文本属性:不要在layout文件或者代码中直接为文本控件设置text属性值。
– 在不同的位置多次使用相同的文本,如果需要修改文本则会引起多处修改。
– APP不能通过为文本提供一份翻译列表就可以适用新的语言的用户,而有很多工具可以快速的完成提供翻译列表的操作。
– 好的做法是,把text属性的文本值定义在string.xml文件中,方便国际化拓展。

SetTextI18n

TextView国际化:在调用TextView.setText给TextView赋值时,不能使用Number.toString例如图中的Integer.toString把数字转为字符串赋值,因为Number.toString不能正确处理分隔符和特定语言环境中的数字。

建议使用 String.format 指定合适的占位符进行赋值。

不能直接使用文本给TextView.setText,具体参照HardcodedText的说明。代码中可以使用@SuppressLint(“SetTextI18n”) 禁用lint的这项检查。

RelativeOverlap

RelativeOverlap是指RelativeLayout位于同一水平方向的两个Widget分别位于layout的左右两边,又都没有限制控件的长度,随着内容的增长,两个控件中间的距离不断缩小,最后会有部分重叠。所以,要控制一下边界。

Bidrrectional Text 双向文本

RtlEnabled

在API 17或更高版本上使用RTL属性需要在manifest文件的application标签中设置android:supportsRtl=”true”。如果已经开始在layout文件中加入RTL属性,但是没有完全使用RTL替代旧版的属性设置,可以设置android:supportsRtl=”false”规避lint的检查。

RtlCompat

API 17以后给文本控件提供了一个textAlignment属性来控制水平方向文字的对齐方式。但是,如果APP支持的版本包含小于 API 17 的版本,那么必须要设置gravity或者layout_gravity属性,因为老版本的系统会忽略textAlignment属性值。

RtlHardcoded

强制方向设置:在文本对齐和控件对齐中使用Gravity.LEFT/Gravity.RIGHT 的方式指定在左侧或者右侧对齐的情形,或在文字从右往左书写的国家或者地区造成困扰。使用Gravity.START/Gravity.END 就可以解决这个问题。同理,在layout文件中设置gravity/layout_gravity 属性时使用start/end 替代left/right

属性paddingLeft/paddingRight和属性layout_marginLeft/layout_marginRight也需要替换为paddingStart/paddingEndlayout_marginStart/layout_marginEnd

如果APP支持的最小API版本小于 API 17,那么需要同时提供left/right属性和start/end属性,因为低版本的系统会忽略start/end属性。

RtlSymmetry

margin|padding左右对称:如果对一个layout对象指定一边的内边距或外边距,那么应该对另一边指定同样大小的内边距或外边距。

参考文献

使用Android Studio Lint静态分析(三)

主要内容

  1. 在终端通过Gradle命令执行Lint检查。
  2. 在编译时进行lint检查。
  • Android Studio中Project目录下有两个文件gradlewgradlew.bat分别是在Mac/Linux系统和Windows系统上的Gradle命令工具。在Mac系统Project目录下执行./gradlew -p ${module_dir} lint命令,就可以执行对指定module按照Android Studio默认支持的扫描规则进行Lint检查。

  • Gradle提供了名为lintOptions的插件对Lint扫描进行个性化配置。

Gradle文件中的Lint配置

在${module_dir}/build.gradle文件android块内使用DSL对象lintOptions对Lint进行配置,配置项可以参考Android Plugin DSL Reference: LintOptions

android {
    lintOptions {
        // true--关闭lint报告的分析进度
        quiet true
        // true--错误发生后停止gradle构建
        abortOnError false
        // true--只报告error
        ignoreWarnings true
        // true--忽略有错误的文件的全/绝对路径(默认是true)
        //absolutePaths true
        // true--检查所有问题点,包含其他默认关闭项
        checkAllWarnings true
        // true--所有warning当做error
        warningsAsErrors true
        // 关闭指定问题检查
        disable 'TypographyFractions','TypographyQuotes'
        // 打开指定问题检查
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // 仅检查指定问题
        check 'NewApi', 'InlinedApi'
        // true--error输出文件不包含源码行号
        noLines true
        // true--显示错误的所有发生位置,不截取
        showAll true
        // 回退lint设置(默认规则)
        lintConfig file("default-lint.xml")
        // true--生成txt格式报告(默认false)
        textReport true
        // 重定向输出;可以是文件或'stdout'
        textOutput 'stdout'
        // true--生成XML格式报告
        xmlReport false
        // 指定xml报告文档(默认lint-results.xml)
        xmlOutput file("lint-report.xml")
        // true--生成HTML报告(带问题解释,源码位置,等)
        htmlReport true
        // html报告可选路径(构建器默认是lint-results.html )
        htmlOutput file("lint-report.html")
        //  true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
        checkReleaseBuilds true
        // 在发布版本编译时检查(即使不包含lint目标),指定问题的规则生成崩溃
        fatal 'NewApi', 'InlineApi'
        // 指定问题的规则生成错误
        error 'Wakelock', 'TextViewEdits'
        // 指定问题的规则生成警告
        warning 'ResourceAsColor'
        // 忽略指定问题的规则(同关闭检查)
        ignore 'TypographyQuotes'
    }
}

部分配置项的说明

下面前三条所涉及的配置项,后面跟的参数都是issue_id,这些ID值跟lint.xml文件中使用的ID是同一个集合。

  1. 检查结果警报级别四个:fatal、error、warning、ignore。
  2. 检查结果输出方式三种:textReport、htmlReport、xmlReport,每一种都有开关并可以指定输出位置。后两种默认放在${module_dir}/build/reports/目录下。
  3. enable、disable是对某些检查做临时的开关设置。
  4. lintConfig指定一个lint文件,文件的内容格式与使用Android Studio Lint静态分析(二)中的lint.xml一样,文件名不必指定为lint.xml,文件路径传给lintConfig的file做参数。

配置本地编译时执行lint检查

配置Gradle脚本可实现编译Android工程时执行Lint检查:好处是既可以尽早发现问题,又可以有强制性;缺点是对编译速度有一定的影响。

编译Android工程执行的是assemble任务,让assemble依赖lint任务,即可在编译时执行Lint检查;同时配置LintOptions,发现Error级别问题时中断编译。

  • 在Application模块的gradle中加入配置
android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def lintTask = tasks["lint${variant.name.capitalize()}"]
        output.assemble.dependsOn lintTask
    }
}
  • 在library模块的gradle中加入配置
android.libraryVariants.all { variant ->
    variant.outputs.each { output ->
        def lintTask = tasks["lint${variant.name.capitalize()}"]
        output.assemble.dependsOn lintTask
    }
}

参考文章

使用Android Studio Lint静态分析(二)

主要内容

在使用Android Studio提供的工具配置了扫描范围和检查项之后,通过lint.xml文件列出issue列表,对静态扫描进行补充。属于手动运行检查中的步骤,参照使用Android Studio Lint静态分析(一)

文件名约定:
1. 把Inspections对话框中配置生成的文件命名为inspection.xml。
2. lint.xml文件名是固定的。

❣️特别提醒:Inspections对话框中的配置,保存在工程目录下的 .idea/inspectionProfiles/inspection.xml 中。

<component name="InspectionProjectProfileManager">
  <profile version="1.0">
    <option name="myName" value="Default" />
    <inspection_tool class="AndroidLintUnusedResources" enabled="false" level="ERROR" enabled_by_default="false" />
  </profile>
</component>
  1. enabled:是否对此项进行检查
  2. level: 问题警告级别
  3. class: class的值与lint.xml文件中的issue.id是对应的, class=AndroidLint${issue.id}

使用lint.xml文件

lint.xml文件的文件名是固定的,放在项目的根目录。lint.xml文件内定义的规则,是对已设置的检查项做排除–禁用某些已经启用的检查项。

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- Disable the given check in this project -->
    <issue id="UnusedResources" severity="ignore"/>
    <issue id="Deprecated" severity="warning">
        <ignore regexp="singleLine"/>
        <ignore path="aaa.txt" />
    </issue>
</lint>

每一个issue标签指定一个规则。指定禁用规则的方法有两种:
1. 直接使用severity=”ignore”,指定禁用规则下所有情况;
2. 使用ignore标签,通过路径或者正则式,指定禁用规则的部分情况;

lint.xml支持的规则id集合

Android Studio支持的检查项是Android Lint的超集。

lint.xml支持的规则包含在Android->Lint内。如图二、图三所示,Android Lint支持的规则有🤙种类型共304个小项。

类型 数量 检查项
Accessibility 5 ClickableViewAccessibility、ContentDescription、GetContentDescriptionOverride、KeyboardInaccessibleWidget、LabelFor
Internationalization 9 ByteOrderMark、EnforceUTF8、HardcodedText、RelativeOverlap、RtlCompat、RtlEnabled、RtlHardcoded、RtlSymmetry、SetTextI18n
Performance 32 AnimatorKeep、DevModeObsolete、DisableBaselineAlignment、DrawAllocation、DuplicateDivider、FloatMath、HandlerLeak、InefficientWeight、LogConditional、MergeRootFrame、NestedWeights、ObsoleteLayoutParam、ObsoleteSdkInt、Overdraw、Recycle、StaticFieldLeak、TooDeepLayout、TooManyViews、UnpackedNativeCode、UnusedIds、UnusedResources、UseCompoundDrawables、UseOfBundledGooglePlayServices、UseSparseArrays、UseValueOf、UselessLeaf、UselessParent、VectorPath、ViewHolder、ViewTag、WakelockTimeout、WearableBindListener
Security 34 AddJavascriptInterface、JavascriptInterface、AllowAllHostnameVerifier、AllowBackup、AuthLeak、BadHostnameVerifier、EasterEgg、ExportedContentProvider、ExportedPreferenceActivity、ExportedReceiver、ExportedService、GetInstance、GrantAllUris、HardcodedDebugMode、HardwareIds、InvalidPermission、PackageManagerGetSignatures、PackagedPrivateKey、SSLCertificateSocketFactoryCreateSocket、SSLCertificateSocketFactoryGetInsecure、SecureRandom、SetJavaScriptEnabled、SetWorldReadable、SetWorldWritable、SignatureOrSystemPermissions、TrustAllX509TrustManager、UnprotectedSMSBroadcastReceiver、UnsafeDynamicallyLoadedCode、UnsafeNativeCodeLocation、UnsafeProtectedBroadcastReceiver、UseCheckPermission、UsingHttp、WorldReadableFiles、WorldWriteableFiles
Usability 14 AlwaysShowAction、AppLinkUrlError、BackButton、ButtonCase、ButtonOrder、ButtonStyle、GoogleAppIndexingApiWarning、GoogleAppIndexingWarning、MenuTitle、NegativeMargin、SelectableText、SmallSp、TextFields、ViewConstructor
Usability.Typography 6 AllCaps、TypographyDashes、TypographyEllipsis、TypographyFractions、TypographyOther、TypographyQuotes
Usability.Icons 18 ConvertToWebp、GifUsage、IconColors、IconDensities、IconDipSize、IconDuplicates、IconDuplicatesConfig、IconExpectedSize、IconExtension、IconLauncherShape、IconLocation、IconMissingDensityFolder、IconMixedNinePatch、IconNoDpi、IconXmlAndPng、MipmapIcons、MissingApplicationIcon、WebpUnsupported
Correctness 173 AaptCrash、AccidentalOctal、AdapterViewChildren、AppCompatCustomView、AppCompatMethod、AppCompatResource、AppIndexingService、AppLinksAutoVerifyError、AppLinksAutoVerifyWarning、ApplySharedPref、Assert、BatteryLife、CheckResult、CommitPrefEdits、CommitTransaction、CustomViewStyleable、CutPasteId、DefaultLocale、Deprecated、DeviceAdmin、DuplicateActivity、DuplicateDefinition、DuplicateIds、DuplicateIncludedIds、DuplicatePlatformClasses、DuplicateUsesFeature、EllipsizeMaxLines、ExifInterface、ExtraText、FindViewByIdCast、FontValidationError、FontValidationWarning、FullBackupContent、GetLocales、GradleCompatible、GradleDependency、GradleDeprecated、GradleDynamicVersion、GradleGetter、GradleIdeError、GradleOverrides、GradlePath、GradlePluginVersion、GridLayout、HalfFloat、HighAppVersionCode、IllegalResourceRef、ImpliedTouchscreenHardware、InOrMmUsage、IncludeLayoutParam、IncompatibleMediaBrowserServiceCompatVersion、InconsistentArrays、InconsistentLayout、InflateParams、InlinedApi、InnerclassSeparator、InstantApps、InvalidAnalyticsName、InvalidId、InvalidImeActionId、InvalidResourceFolder、InvalidUsesTagAttribute、InvalidVectorPath、InvalidWearFeatureAttribute、JobSchedulerService、LibraryCustomView、LocalSuppress、LocaleFolder、LogTagMismatch、LongLogTag、MangledCRLF、ManifestOrder、ManifestResource、MergeMarker、MinSdkTooLow、MissingBackupPin、MissingConstraints、MissingFirebaseInstanceTokenRefresh、MissingId、MissingIntentFilterForMediaSearch、MissingLeanbackLauncher、MissingLeanbackSupport、MissingMediaBrowserServiceIntentFilter、MissingOnPlayFromSearch、MissingPermission、MissingPrefix、MissingSuperCall、MissingTvBanner、MissingVersion、MockLocation、MultipleUsesSdk、NamespaceTypo、NestedScrolling、NetworkSecurityConfig、NewApi、NewerVersionAvailable、NfcTechWhitespace、NotInterpolated、NotSibling、ObjectAnimatorBinding、OldTargetApi、OnClick、Orientation、Override、OverrideAbstract、ParcelClassLoader、ParcelCreator、PendingBindings、PermissionImpliesUnsupportedHardware、PinSetExpiry、PrivateApi、PrivateResource、Proguard、ProguardSplit、PropertyEscape、ProtectedPermissions、PxUsage、Range、RecyclerView、ReferenceType、Registered、RequiredSize、ResAuto、ResourceAsColor、ResourceCycle、ResourceName、ResourceType、RestrictedApi、SQLiteString、ScrollViewCount、ScrollViewSize、SdCardPath、ServiceCast、ShiftFlags、ShortAlarm、ShowToast、SimpleDateFormat、SpUsage、StateListReachable、StopShip、StringShouldBeInt、SupportAnnotationUsage、Suspicious0dp、SuspiciousImport、SwitchIntDef、TestAppLink、TextViewEdits、UniqueConstants、UniquePermission、UnknownId、UnknownIdInLayout、UnlocalizedSms、UnsupportedTvHardware、UnusedAttribute、UseAlpha2、UsesMinSdkAttributes、ValidFragment、ValidRestrictions、VectorDrawableCompat、VectorRaster、VisibleForTests、WearStandaloneAppFlag、WebViewLayout、WifiManagerLeak、WifiManagerPotentialLeak、WrongCall、WrongCase、WrongConstant、WrongFolder、WrongRegion、WrongThread、WrongThreadInterprocedural、WrongViewCast
Correctness.Messages 11 ExtraTranslation、ImpliedQuantity、MissingQuantity、MissingTranslation、PluralsCandidate、StringEscaping、StringFormatCount、StringFormatInvalid、StringFormatMatches、UnusedQuantity、Typos
Correctness.Chrome OS 2 PermissionImpliesUnsupportedChromeOsHardware、UnsupportedChromeOsHardware
合计 304

结束语

规则项目太多而且没有说明,这样阅读比较困难对实际应用没有太多作用,希望后面会有时间整理。

使用Android Studio Lint静态分析(一)

主要内容

使用Android Studio提供的工具,配置Lint扫描范围和检查项。在使用 Lint 改进您的代码文档中,属于手动运行检查

程序静态分析

程序静态分析是指在不运行代码的方式下,通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描,验证代码是否满足规范性、安全性、可靠性、可维护性等指标的一种代码分析技术。

Java-Android代码常用的分析工具

  • Checkstyle
  • FindBugs
  • Soot
  • Lint

使用 Lint 改进您的代码

Lint是Android Studio提供的 代码扫描工具,自动化地对代码进行扫描,帮助改善代码结构的规范性和可维护性,提高代码质量。

Lint静态扫描的工作原理如下图。

Android Studio中Lint的操作步骤

Android Stuido Lint提供了 Specify Inspection Scope 面板,在面板中对代码扫描任务做个性化定义。

打开Specify Inspection Scope面板:菜单栏->Analyze->Inspect Code

设定扫描范围Custom Scope

  • 在面板中可以配置Lint扫描的范围,其中File选项会根据面板弹出前光标所在位置显示三种状态:
    • Files:在打开的文件上调出面板,可以对当前打开的文件进行扫描;
    • Directory:在Project面板中选中名录调出面板,对选定的目录进行扫描;
    • 隐藏:不提供此选项
  • 个性化扫描范围设定
    • 标题右侧的下拉框可以选择已有的自定义扫描配置
    • 下拉框右侧的“…”按钮,可以调起自定义扫描配置面板,可以任意的为一项配置添加和删除待扫描的源文件。左侧一列是自定义的配置列表,他们将出现在上一层的下拉框内备选。
    • 设定完成之后,生成对应的配置文件放在.idea/scopes目录下

检查项设置

Specify Inspection Scope 面板上最后一项Inspection profile,指定在代码扫描过程中对哪些问题进行检查。系统默认只提供一个Default可选项,我们可以点击下拉框右边的“…”按钮添加个性化的可选项。

  • 如下图,我们需要先把Default配置项Copy to Project,改个可爱的名字后就可以在新产生的配置项上进行编辑了。

第一步可以点击橡皮擦(图中被弹出框盖住了),把从Default中复制下来的选项全部清除,然后根据需要选择合适的集合即可。

其中Android、General、XML和Spelling是必选项,如果工程中只有Java代码,就只需要勾选Java一项就可以了,否则可能还需要选上kotlin、flutter、dart等相关的选项。

另外,上面说的那些选项,下面还有许多的子选项,并不是都必须的,可以根据需要选择部分子选项进行检查。

设定完成之后,生成对应的配置文件存放在.idea/inspectionProfiles目录下

  • 问题警告级别

选中检查项设置某一个选项,右侧会给出对应的描述和安全提醒级别。Android Studio Lint提供了六级安全提示,每种级别对应着不同的外观,重要程度不同,醒目的程度也不一样。

设置了所需的选项之后,点击右下角的“OK”,就可以使用这个设置集合了。

执行Lint静态扫描&查看结果

扫描范围和检查项设置完成之后,就可以执行静态扫描了。点击Specify Inspection Scope面板右下角的“OK”按钮,就开始执行扫描操作了。扫描完成之后,Android Studio底部会弹出Inspection Results面板, 可以根据安全级别或者检查项进行归类,到这里我们就可以根据扫描的结果对代码进行相应的整理和修改了。

参考文章