0%

代码覆盖率建设

在缩包过程中,代码逻辑复杂,删除风险高,单纯的lint无用代码扫描很难发现一些无用case下的代码,而且反射机制在这种方式下也存在潜在风险。另外,在模块化开发中,每个模块都会有单独的负责人,建立每个模块的代码覆盖率,也方便建立包大小卡口和责任制裁剪。

1 代码覆盖率达到的效果

当项目implementation一个lib,通过代码覆盖率可以知道这个模块在正式环境里面使用了多少个类,未使用多少类。

2 代码覆盖率建设方案

建立代码覆盖率,需要在app运行时,上报使用过程中的类信息。那如何统计app已经使用的类信息。

2.1 类插桩

打包阶段,通过插桩的方式,想类的静态代码块内插入代码记录类被调用,根据java特性,类被加载会触发静态代码块。 这里有个优化是将类命名为1234,而不是类名,然后保存数字和类的映射关系,在app运行时可以节省存储内存和上传流量。这个类型的开源方案

class_use_analyze

2.2 Hook

通过hook框架,hook classloader的loadClass方法,收集class name然后上报。开源的hook方案也比较多 SandHookepic

2.3 优劣

有了两种方案,那需要对方案对比和选型。

方案一

优点: 稳定性好
缺点: 需要对每个类插桩,导致安装包增加300K。

方案二

优点: 不需要对类插桩导致apk体积增大
缺点: hook框架存在稳定性风险,对于大型的app需要严格评估;loadClass的方式只能记录类名,类名的存储比int类型存储大很多,在app运行内存和数据上报的时候存在性能影响。

3 扩展

我们已经将使用类的信息上报,那可以知道类是否被使用,但是如果可以将类可以分组归类成lib信息,那将更加直观,也可以推动lib缩包。
arsc_info

在我们构建过程中,是可以获取依赖列表的,通过获取lib的依赖文件,然后通过解析aar文件,记录类在哪个aar中,最终服务器可以将class归类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Configuration configuration
try {
// 3.x
configuration = project.configurations."${variant.name}CompileClasspath"
} catch (Exception e) {
// 2.x
configuration = project.configurations."_${variant.name}Compile"
}
configuration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.each {
def identifier = it.module.id
if (identifier.version != "unspecified") {
Set<ResolvedDependency> resolvedDependencies = it.getChildren();
it.allModuleArtifacts.forEach({
ResolvedArtifact artifact = it;

})

} else {
// println(it.)
}

println(identifier.getName() + ":" + identifier.getVersion())
// println("${identifier.group}:${identifier.name}:${identifier.version}")
}

总结

代码覆盖率主要解决安全删除冗余代码,结合开发模式建立模块责任制,保证module模块的代码覆盖率,也及时检测不出业务下线等场景,综合建立一套完整的机制去缩包和保障。