Android静态代码扫描效率优化与实践

image

为了方便查看源码,新建一个工程,在build.gradle脚本中,添加如下依赖:

compile ‘com.android.tools.build:gradle:3.1.1’
compile ‘com.android.tools.lint:lint-gradle:26.1.1’

我们可以得到如下所示的依赖:

image

lint-api-26.1.1

Lint工具集的一个封装,实现了一组API接口,用于启动Lint;

lint-checks-26.1.1

一组内建的检测器,用于对这种描述好Issue进行分析处理;

lint-26.1.1

可以看做是依赖上面两个jar形成的一个基于命令行的封装接口形成的脚手架工程,我们的命令行、Gradle任务都是继承自这个jar包中相关类来做的实现;

lint-gradle-26.1.1

可以看做是针对Gradle任务这种运行方式,基于lint-26.1.1做了一些封装类;

lint-gradle-api-26.1.1

真正Gradle Lint任务在执行时调用的入口;

在理解清楚了以上几个jar的关系和作用之后,我们可以发现Lint的核心库其实是前三个依赖。后面两个其实是基于脚手架,对Gradle这种运行方式做的封装。最核心的逻辑在LintDriver的Analyze方法中。

fun analyze() {

…省略部分代码…

for (project in projects) {
fireEvent(EventType.REGISTERED_PROJECT, project = project)
}
registerCustomDetectors(projects)

…省略部分代码…

try {
for (project in projects) {
phase = 1

val main = request.getMainProject(project)

// The set of available detectors varies between projects
computeDetectors(project)

if (applicableDetectors.isEmpty()) {
// No detectors enabled in this project: skip it
continue
}

checkProject(project, main)
if (isCanceled) {
break
}

runExtraPhases(project, main)
}
} catch (throwable: Throwable) {
// Process canceled etc
if (!handleDetectorError(null, this, throwable)) {
cancel()
}
}
…省略部分代码…
}

主要是以下三个重要步骤:

  • registerCustomDetectors(projects)

Lint为我们提供了许多内建的检测器,除此之外我们还可以自定义一些检测器,这些都需要注册进Lint工具用于对目标文件进行扫描。这个方法主要做以下几件事情:

  1. 遍历每一个Project和它的依赖Library工程,通过client.findRuleJars来找出自定义的jar包;
  2. 通过client.findGlobalRuleJars找出全局的自定义jar包,可以作用于每一个Android工程;
  3. 从找到的jarFiles列表中,解析出自定义的规则,并与内建的Registry一起合并为CompositeIssueRegistry; 需要注意的是,自定义的Lint的jar包存放位置是build/intermediaters/lint目录,如果是需要每一个工程都生效,则存放位置为~/.android/lint/。
  • computeDetectors(project)

这一步主要用来收集当前工程所有可用的检测器。

checkProject(project, main)接下来这一步是最为关键的一步。在此方法中,调用runFileDetectors来进行文件扫描。Lint支持的扫描文件类型很多,因为是官方支持,所以针对Android工程支持的比较友好。一次Lint任务运行时,Lint的扫描范围主要由Scope来描述。具体表现在:

fun infer(projects: Collection?): EnumSet {
if (projects == null || projects.isEmpty()) {
return Scope.ALL
}

// Infer the scope
var scope = EnumSet.noneOf(Scope::class.java)
for (project in projects) {
val subset = project.subset
if (subset != null) {
for (file in subset) {
val name = file.name
if (name == ANDROID_MANIFEST_XML) {
scope.add(MANIFEST)
} else if (name.endsWith(DOT_XML)) {
scope.add(RESOURCE_FILE)
} else if (name.endsWith(DOT_JAVA) || name.endsWith(DOT_KT)) {
scope.add(JAVA_FILE)
} else if (name.endsWith(DOT_CLASS)) {
scope.add(CLASS_FILE)
} else if (name.endsWith(DOT_GRADLE)) {
scope.add(GRADLE_FILE)
} else if (name == OLD_PROGUARD_FILE || name == FN_PROJECT_PROGUARD_FILE) {
scope.add(PROGUARD_FILE)
} else if (name.endsWith(DOT_PROPERTIES)) {
scope.add(PROPERTY_FILE)
} else if (name.endsWith(DOT_PNG)) {
scope.add(BINARY_RESOURCE_FILE)
} else if (name == RES_FOLDER || file.parent == RES_FOLDER) {
scope.add(ALL_RESOURCE_FILES)
scope.add(RESOURCE_FILE)
scope.add(BINARY_RESOURCE_FILE)
scope.add(RESOURCE_FOLDER)
}
}
} else {
// Specified a full project: just use the full project scope
scope = Scope.ALL
break
}
}
}

可以看到,如果Project的Subset为Null,Scope就为Scope.ALL,表示本次扫描会针对能检测的所有范围,相应地在扫描时也

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值