保持优步移动应用程序的可靠性至关重要,以促进无缝和愉快的用户体验。沿着一个健壮的插件架构那特色标志,外部数据的动态验证,静态分析工具在确保高代码可靠性方面扮演着关键角色,它在向用户发布更新之前检测潜在的bug。
此前,优步使用第三方静态分析工具来检测潜力nullpointerexceptions.(NPE),这是导致应用崩溃的主要原因,并在我们的Android代码库中保持可靠性。然而,随着我们代码库的增长,我们发现这些工具并不能完全满足我们为工程师提供强有力的检查和快速反馈的需求。
要解决这个问题,优步开发了NullAway,快速实用的工具,可以帮助消除NPES。Nullaway显着提高了开发人员的生产力,同时保持了我们安全部署所需的强大检查。现在,我们为开源社区贡献了这个工具,所以其他人也可以建立更可靠的应用程序!
在本文中,我们将讨论开发NullAway背后的动机,概述我们如何构建这个工具,并提供如何在您自己的Android应用程序和Java项目中使用它的说明。
淘淘背后的动机
移动崩溃可能对用户造成重大问题,例如防止骑手及时请求旅行或驾驶员接受乘坐。NPES发生在Java中未注释的NULL指针时发生的NPE,是Android应用程序中崩溃的频繁原因。我们在优步的策略一直使用静态代码分析工具来防止NPE尽可能地崩溃。2016年,优步部署了Facebook推断和根除用于静态检测潜在NPE的工具。除了我们旁边运行时注释验证引擎(RAVE)验证,这些工具将我们在生产应用中观察到的NPEs数量减少了一个数量级。
为了最大限度地提高可靠性,我们希望保证没有代码可以合并到我们的应用中,直到所有可能的NPE警告都是固定的;这样,我们的主分支将始终处于“绿色”状态(意味着构建已通过所有相关测试),并具有零警告。获取此保证需要在我们的情况下运行这些工具提交队列,这是我们持续集成管道的最后阶段,这样来自工具的任何错误都会阻止新的代码合并。
虽然提高应用程序可靠性,但在提交队列上运行错误查找工具,如提交队列上的NPE检查器有两个副作用,鼓励我们创造性地思考我们的代码:
- 迟到的反馈:对于患有NPE问题的差异,开发人员仅在提出代码变更的最后阶段收到警告。这次迟到的反馈可能导致令人沮丧的经历:开发人员的变化可以通过代码审查和所有其他检查,这只是由于易于修复的问题,仅在提交队列中拒绝。开发人员可能会通过这一点移动到另一个任务,并且必须必须上下文切换返回代码并解决警告。
- 更高的延迟,降低开发人员生产力:Submit Queue体验的整体延迟增加了,从而降低了开发人员的生产力。由于许多差异在提交队列中失败,并带有NPE警告,它们需要多次提交,从而增加了总体队列长度。
我们的开发速度(一次飞行中的许多差异)以及现有的NPE检查器的冗长运行时间阻止了我们在此过程中早期运行它们,因此我们决定建立一个新的解决方案,足以以低延迟运行的快速运行在我们的开发管道的每个阶段,即使在本地构建期间也是如此。以及开发自己的修复的额外好处是我们可以节省机器资源,因为我们继续发展Codebase。
我们的答案?nullaway。
引入NullAway
NullAway的核心是一个开源的基于类型的Java代码NPE检查器。要使用NullAway,必须先添加@nullable.代码中任何字段、方法参数或返回值可能为空的地方的注释。(由于我们之前使用了Eradicate,所以在我们的代码库中已经有了这些注释。)给出这些注释后,NullAway执行一系列本地一致性检查,以确保代码中被解引用的任何指针都不能为空。
nullaway是作为插件支票构建的容易出错发现框架。容易出错运行代码检查作为标准Java编译过程的一部分。此编译器集成允许检查重复使用编译器已完成的大部分工作,例如代码解析和类型检查。此外,Nullaway和错误易于直接集成到快速,内存的并行构建中buck,我们用于我们的Android代码的构建工具。因此,NULLAWAY可以比在正常构建过程之外运行的工具更快地运行。
我们发现,Nullaway仅在正常构建时间(我们的测量中约为10%)添加了一个小的开销。因此,我们仅在提交队列上运行,而是配置了Nullaway以在我们的Android代码的每一个构建中运行。
将nullaway集成到我们所有Java构建中的价值是三倍:
- 立即反馈:集成到所有构建中,开发人员可以在引入潜在的NPE时立即获得反馈,而不是必须等待提交队列。
- 没有NPE检查器:构建集成意味着我们不再需要将空值检查器作为一个单独的作业运行在Submit Queue上,从而节省大量的机器资源。
- 降低提交队列延迟:Nullaway可以显着降低提交队列延迟,因为由于NPE警告引起的提交队列的失败变得非常罕见;在我们移动后,这种降低的延迟变得更加明显Monorepo.。
你唱无红色
要了解nullaway如何工作,让我们考虑它如何确定程序中的某些表达式是否可以为空,这是常用的Nullness检查所需的。考虑以下示例:
A类{
@Nullable对象f;
}
静态空穴m(A x) {
if(x.f!= null){
System.out.println (x.f.toString ());
}
}
在上面的例子中,nullaway试图展示X不是null,确保这一点X.F.没有造成npe,也没有那个X.F.不是空的调用x.f.toString ()。对于不应该为空的表达式,nullaway首先尝试快速证明表达式是否明显为非空,例如,通过检查其类型是否为非空@nullable.或者如果是表达式新对象()这不能为空。例如,自称宣言以来X没有注释@nullable.,Nullaway可以假设它不是空,从而显示X.F.是安全的。NullAway通过检查a@nullable.表达式永远不会作为参数传递给方法m。
如果快速检查失败,则nullaway尝试使用非无效性数据流分析,利用现有的图书馆从检查框架。数据流分析的关键目的是在代码中发现现有的NULL检查。为了x.f.toString ()在上面的例子中致电,快速检查无法显示X.F.不能为空,因为A.F.领域是@nullable.。然而,我们的数据流分析使用了附加条件检查x.f!= null为了显示x.f.toString ()电话是安全的。由于数据流分析的代价很高(它需要计算一个控制流图并运行一个定点计算),所以NullAway对每个方法只运行一次分析,并缓存结果。
上nullaway github页面,有详细说明如何在Android应用程序或其他Java代码上运行该工具。有关Nullaway检查,错误消息和限制的更多详细信息,请参阅我们的详细实施指南。
下一步
通过使用NullAway,我们可以通过其他工具对npe进行强大的静态检查,同时还可以提高开发人员的工作效率。我们希望您发现NullAway有助于提高Java代码库的可靠性。
有兴趣开发开源工具来提高应用程序的可靠性和开发人员的生产力吗?申请一个角色超级的手机开发者体验平台团队。
马努·斯里达兰(Manu Sridharan)是优步移动开发者平台团队的软件工程师,负责在优步的代码库中应用静态分析。今年早些时候,他在纽约咖喱关于如何利用静态分析和软件架构来提高Uber的应用质量。






