关于android:Android-Lint-实践-简介及常见问题分析

43次阅读

共计 6186 个字符,预计需要花费 16 分钟才能阅读完成。

详情

QMUI Android 之前更新了 1.0.4 版本,其中次要的个性是引入了 Android Lint,对我的项目代码进行优化。Android Lint 是 SDK Tools 16(ADT 16)开始引入的一个代码扫描工具,通过对代码进行动态剖析,能够帮忙开发者发现代码品质问题和提出一些改良倡议。除了查看 Android 我的项目源码中潜在的谬误,对于代码的正确性、安全性、性能、易用性、便利性和国际化方面也会作出查看。

而最终抉择了 Android Lint 作为我的项目的代码检测工具,是因为它具备以下几个个性:

  • 曾经被集成到 Android Studio,使用方便。
  • 能在编写代码时实时反馈出潜在的问题。
  • 能够自定义规定。Android Lint 自身蕴含大量曾经封装好的接口,能提供丰盛的代码信息,开发者能够基于这些信息进行自定义规定的编写。

开始应用

Android Lint 的工作过程比较简单,一个根底的 Lint 过程由 Lint Tool(检测工具),Source Files(我的项目源文件)和 lint.xml(配置文件)三个局部组成,Lint Tool 读取 Source Files,依据 lint.xml 配置的规定(issue)输入后果(如下图)。

如下面所形容,在 Android Studio 中,Android Lint 曾经被集成,只须要点击菜单 —— Analyze —— Inspect Code 即可运行 Android Lint,在弹出的对话框中能够设置执行 Lint 的范畴,能够抉择整个我的项目,也能够只抉择以后的子模块或者其余自定义的范畴:

查看结束后会弹出 Inspection 的控制台,并在其中列出具体的查看后果:

如上图所展现的,Android Lint 对查看的后果进行了分类,同一个规定(issue)下的问题会聚合,其中针对 Android 的规定类别会在分类前阐明是 Android 相干的,次要是六类:

  • Accessibility 无障碍 ,例如 ImageView 短少 contentDescription 形容,String 编码字符串等问题。
  • Correctness 正确性 ,例如 xml 中应用了不正确的属性值,Java 代码中间接应用了超过最低 SDK 要求的 API 等。
  • Internationalization 国际化 ,如字符短少翻译等问题。
  • Performance 性能 ,例如在 onMeasureonDraw 中执行 new,内存泄露,产生了冗余的资源,xml 构造冗余等。
  • Security 安全性 ,例如没有应用 HTTPS 连贯 Gradle,AndroidManifest 中的权限问题等。
  • Usability 易用性 ,例如短少某些倍数的切图,反复图标等。

其余的后果条目则是针对 Java 语法的问题,另外每一个问题都有辨别重大水平(severity),从高到底顺次是:

  • Fatal
  • Error
  • Warning
  • Information
  • Ignore

其中 FatalError 都是指谬误,然而 Fatal 类型的谬误会间接中断 ADT 导出 APK,更为严重。另外如下图所示,在后果列表中点击一个条目,能够看到具体的源文件名和地位,以及命中的谬误规定(issue)、解决方案或者屏蔽提醒:

上图的例子是在 ScrollView 的第一层子元素中设置了高度为 match_parent,Android Lint 会间接给出解决办法——应用 wrap_content 代替,大部分动态语法相干的问题 Android Lint 都能够间接给出解决办法。

除了间接在菜单中运行 Lint 外,大部分问题代码在编写时 Android Studio 就会给出揭示:

配置

对于执行 Lint 操作的相干配置,是定义在 gradle 文件的 lintOptions 中,可定义的选项及其默认值包含(翻译自 LintOptions – Android Plugin 2.3.0 DSL Reference):

android {
    lintOptions {
        // 设置为 true,则当 Lint 发现错误时进行 Gradle 构建
        abortOnError false
        // 设置为 true,则当有谬误时会显示文件的全门路或绝对路径 (默认状况下为 true)
        absolutePaths true
        // 仅查看指定的问题(依据 id 指定)check 'NewApi', 'InlinedApi'
        // 设置为 true 则查看所有的问题,包含默认不查看问题
        checkAllWarnings true
        // 设置为 true 后,release 构建都会以 Fatal 的设置来运行 Lint。// 如果构建时发现了致命(Fatal)的问题,会停止构建(具体由 abortOnError 管制)checkReleaseBuilds true
        // 不查看指定的问题(依据问题 id 指定)disable 'TypographyFractions','TypographyQuotes'
        // 查看指定的问题(依据 id 指定)enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // 在报告中是否返回对应的 Lint 阐明
        explainIssues true
        // 写入报告的门路,默认为构建目录下的 lint-results.html
        htmlOutput file("lint-report.html")
        // 设置为 true 则会生成一个 HTML 格局的报告
        htmlReport true
        // 设置为 true 则只报告谬误
        ignoreWarnings true
        // 从新指定 Lint 规定配置文件
        lintConfig file("default-lint.xml")
        // 设置为 true 则错误报告中不包含源代码的行号
        noLines true
        // 设置为 true 时 Lint 将不报告剖析的进度
        quiet true
        // 笼罩 Lint 规定的重大水平,例如:severityOverrides ["MissingTranslation": LintOptions.SEVERITY_WARNING]
        // 设置为 true 则显示一个问题所在的所有中央,而不会截短列表
        showAll true
        // 配置写入输入后果的地位,格局能够是文件或 stdout
        textOutput 'stdout'
        // 设置为 true,则生成纯文本报告(默认为 false)textReport false
        // 设置为 true,则会把所有正告视为错误处理
        warningsAsErrors true
        // 写入检查报告的文件(不指定默认为 lint-results.xml)xmlOutput file("lint-report.xml")
        // 设置为 true 则会生成一个 XML 报告
        xmlReport false
        // 将指定问题(依据 id 指定)的重大级别(severity)设置为 Fatal
        fatal 'NewApi', 'InlineApi'
        // 将指定问题(依据 id 指定)的重大级别(severity)设置为 Error
        error 'Wakelock', 'TextViewEdits'
        // 将指定问题(依据 id 指定)的重大级别(severity)设置为 Warning
        warning 'ResourceAsColor'
        // 将指定问题(依据 id 指定)的重大级别(severity)设置为 ignore
        ignore 'TypographyQuotes'
    }
}

lint.xml 这个文件则是配置 Lint 须要禁用哪些规定(issue),以及自定义规定的重大水平(severity),lint.xml 文件是通过 issue 标签指定对一个规定的管制,在我的项目根目录中建设一个 lint.xml 文件后 Android Lint 会自动识别该文件,在执行查看时依照 lint.xml 的内容进行查看。如下面提到的那样,开发者也能够通过 lintOptions 中的 lintConfig 选项来指定配置文件。一个 lint.xml 示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- Disable the given check in this project -->
    <issue id="HardcodedText" severity="ignore"/>
    <issue id="SmallSp" severity="ignore"/>
    <issue id="IconMissingDensityFolder" severity="ignore"/>
    <issue id="RtlHardcoded" severity="ignore"/>
    <issue id="Deprecated" severity="warning">
        <ignore regexp="singleLine"/>
    </issue>
</lint>

issue 标签中应用 id 指定一个规定,severity="ignore" 则表明禁用这个规定。须要留神的是,某些规定能够通过 ignore 标签指定仅对某些属性禁用,例如下面的 Deprecated,示意查看是否有应用不举荐的属性和办法,而在 issue 标签中包裹一个 ignore 标签,在 ignore 标签的 regexp 属性中应用正则表达式指定了 singleLine,则表明对 singleLine 这个属性屏蔽查看。

另外开发者也能够应用 @SuppressLint(issue id) 标注针对某些代码疏忽某些 Lint 查看,这个标注既能够加到成员变量之前,也能够加到办法申明和类申明之前,别离针对不同范畴进行屏蔽。

常见问题

咱们在应用 Android Lint 对我的项目进行查看后,整顿了一些问题及解决办法,上面列举较为常见的场景:

ScrollView size validation

这也是上文提到过的一个状况,在 ScrollView 的第一层子元素中设置了高度为 match_parent,这是谬误的写法,实际上在 measure 时这里必定会被当作 wrap_content 去解决,因而依照 Lint 的倡议,间接改为 wrap_content 即可。

Handler reference leaks

Handler 援用的内存泄露问题,例如上面的例子:

protected static final int STOP = 0x10000;
protected static final int NEXT = 0x10001;

@BindView(R.id.rectProgressBar) QMUIProgressBar mRectProgressBar;
@BindView(R.id.circleProgressBar) QMUIProgressBar mCircleProgressBar;

int count;

private Handler myHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {super.handleMessage(msg);
        switch (msg.what) {
            case STOP:
                break;
            case NEXT:
                if (!Thread.currentThread().isInterrupted()) {mRectProgressBar.setProgress(count);
                    mCircleProgressBar.setProgress(count);
                }
        }
    }
};

首先非动态的外部类或者匿名类会隐式的持有其外部类的援用,外部类应用了外部类的办法 / 成员变量也会导致其持有外部类援用,因而下面这种状况会导致 handler 持有了外部类,外部类同时持有 handler,handler 是异步的,当 handler 的音讯发送进来后,外部类因 hanlder 的持有而无奈销毁,最终导致内存泄露。

解决办法则是把该外部类改为 static,外部类中应用的外部类办法 / 成员变量改为弱援用,具体如下:

@BindView(R.id.rectProgressBar) QMUIProgressBar mRectProgressBar;
@BindView(R.id.circleProgressBar) QMUIProgressBar mCircleProgressBar;
    
int count;

private ProgressHandler myHandler = new ProgressHandler();

@Override
protected View onCreateView() {myHandler.setProgressBar(mRectProgressBar, mCircleProgressBar);
}
    
private static class ProgressHandler extends Handler {
    private WeakReference<QMUIProgressBar> weakRectProgressBar;
    private WeakReference<QMUIProgressBar> weakCircleProgressBar;

    public void setProgressBar(QMUIProgressBar rectProgressBar, QMUIProgressBar circleProgressBar) {weakRectProgressBar = new WeakReference<>(rectProgressBar);
        weakCircleProgressBar = new WeakReference<>(circleProgressBar);
    }

    @Override
    public void handleMessage(Message msg) {super.handleMessage(msg);
        switch (msg.what) {
            case STOP:
                break;
            case NEXT:
                if (!Thread.currentThread().isInterrupted()) {if (weakRectProgressBar.get() != null && weakCircleProgressBar.get() != null) {weakRectProgressBar.get().setProgress(msg.arg1);
                        weakCircleProgressBar.get().setProgress(msg.arg1);
                    }
                }
        }

    }
}

Memory allocations within drawing code

onMeasure、onDraw 都是被频繁调用的办法,因而 Lint 不倡议在其中执行 new 操作,能够在 onCreateView 等非频繁调用的机会进行 new 操作,并用成员变量保留,再在 onMeasure 中应用成员变量。

‘private’method declared‘final’

private static final void addLinkMovementMethod(TextView t) {MovementMethod m = t.getMovementMethod();
    
    // ...
}

如下面的示例代码,会产生‘private’method declared‘final’的正告,因为公有办法是不会被 override 的,因而齐全没有必要申明 final

参考资料:

应用 Lint 改良您的代码 | Android Studio
LintOptions – Android Plugin 2.3.0 DSL Reference
Android Lint Checks – Android Studio Project Site

正文完
 0