迁移中的工程稳定性:在Uber的Android应用程序中迁移到不可变的集合

0.
迁移中的工程稳定性:在Uber的Android应用程序中迁移到不可变的集合

制作Android应用程序的模型经常涉及手动编写大量的样板代码。此过程可能是耗时的,容易出错,并且在不正确完成时导致难以点击的错误。要在优步解决此问题,我们构建了一个自定义堆栈以生成autovalue.模型和网络客户端节俭规格

在本文中,我们探讨了优步的Android应用程序中的型号尽管使用自动覆盖,最终最终成为可变的模式;我们说明了一些可变性和细节通过使用可变造成的错误的缺陷集合课程在我们的自动覆盖模型中;最后,我们解释了我们如何安全地执行大量迁移,这些迁移没有编译时的安全性,以移动到不可变的收集类,而不会影响我们的应用程序的稳定性。

Uber的Android模型生成堆栈

即使我们的应用程序的运输机制是JavaScript对象表示法(JSON),我们使用节约在我们的模型生成堆栈中,以定义客户端和后端之间的共享规范。这使得服务调用更加一致并防止服务定义变得不同步。为我们的Android应用程序生成模型,节俭用于解析规格和javapoet.用于创建autovalue类。

Uber的Android模型生成堆栈使用自动扫描以避免变形性。

自动覆盖使我们能够轻松生产不可变价值课程通过创建抽象类并用诠释@autovalue.。然后自动处理在抽象类上运行其注释处理器以生成它们的备份实现。但是,如果您不小心,这些模型可能无意中变得可变并导致错误。在我们的自动覆盖模型中包括集合类时,我们遇到了无意的可变性。

自动模型中的可变性

下面的骑手模型是从节俭规范生成的自动调整模型:

骑手式模特;代表骑手(请求优步的人)的模型。

由于该模型定义了使用集合接口的属性列表地图,除非模型是创建的,否则它并非真正不变Immutablelist.ImmutableMap.实施。

我们通过网络发送骑手模型JavaScript对象表示法(JSON)并与之反驳g。开箱即用,GSON在反序列化网络响应时返回Collection接口的可变实现。这让我们允许我们编写代码,这些代码是我们认为“不可变的模型中的收集。能够在模型中进行混合收集导致错误难点,难以重现。

在我们支持时,在我们的应用程序中经常出现的一种错误反应数据流来自模型的集合。当这些集合将被修改为填充视图时,会出现麻烦。这pricedvehicles.类显示了一个例子:

反应流突变;一类提供通过反应流API具有价格的车辆流。

pricedvehicles.班级过滤出没有价格的车辆veviclestream,和剩余的车辆(有价格)的剩余车辆通过pricedvehlicles.stream()API。这里的问题是pricedvehlicles.filtervehicleswithoutprice()突变背部的车载列表veviclestream。所以veviclestream之后不会退回所有车辆filterdhicleswithoutprice()被称为。

这导致对用户显示的数据不一致,因为在视图之间共享流。由于它要求用户以特定顺序访问视图,因此可以难以追踪这些错误。在这种情况下,仅在此情况下显示不一致的数据pricedvehlicles.filtervehicleswithoutprice()在想要显示所有车辆的视图之前被调用veviclestream

如果我们的模型使用了不可变的收集类,则不会发生这些类型的错误;不可变的物体在类之间共享是安全的,因为它们无法修改。为了额外澄清他们为什么安全分享,我们建议阅读有效的Java第二版:项目15最小化可变性由Joshua块。

搬到不可变的收藏品

我们希望我们的模型返回不可变的收集类,而不是可变的集合。在优步,我们使用一个小叉郭瓦这为我们提供了不可变的收藏品。下面的骑手模型指定ImmutableMap.Immutablelist.应该返回什么时候PaymentProfiles()第三个是叫做:

骑手与不可变的收藏师我们的骑手模型,使用不变的集合。

但是,从收集界面转移到不可变的混凝土类并不像使我们所有的模型都回来那么简单Immutablelist.ImmutableMap.。这些类在修改时,这些类别不会对突变和运行时崩溃的任何编译时间安全性。这是因为番石榴的收藏UnsupportedoperationException.当调用突变状态的集合方法时。

回想一下,番石榴的不可变的集合实现了java.util收集接口,这些接口定义了方法添加()去掉()。为了确保在移动到不可变的具体课程后不会发生崩溃,我们必须审查整个应用程序。这并不实用,因为我们的应用程序有数百个贡献者。

安全地迁移:跟踪可变的集合

要迁移到不可变的集合类而无需崩溃生产用户,我们在网络序列化层中进行了API看不见的变化(通过Gson的裂缝返回每个Java.util Collection接口的特殊实现。这使我们可以在模型API中使用集合接口,但是在带有跟踪的可变集合(TMC)的引擎盖下返回它们。

TMC是一种特殊的集合实现,允许我们在我们的应用程序的调试版本中的集合中突变时崩溃并登录发布。要在我们的应用程序的调试变体中崩溃,我们使用了Timber.Tree.rethrows timber.e()调用。我们的跟踪可变的集合GIST显示我们的每个TMC和典型物料用来创建它们。您会注意到每个TMC呼叫timber.e()调用突变方法时。

通过测井生产中的突变,我们能够检测到收集的所有地方,而不会手动审核整个应用程序。经过两个月后,我们停止看到正在生产的突变,并移动所有模型来使用不可变的集合而不是收集接口。由于TMCS为我们提供了高度的安全性,因此这一镜头迁移使我们能够切换到混凝土的不变等级,而不会影响生产中的任何用户。

使用自定义堆栈来生成模型和网络客户端,使我们能够在所有应用程序中快速制作模型API。像这样的大迁移经常涉及工程安全到过程中,通常以独特的方式。自从搬到不可变的收藏以来,我们投资于在编译时在模型中捕获收集突变静态分析

如果您有兴趣应对数百万用户和数百个贡献者的缩放软件的挑战,请考虑加入我们的队伍

Warren Smith是Uber在移动应用程序架构上工作的工程师。Molly Vorwerck是Uber工程博客的技术编辑器。ti8 竞猜雷竞技app雷竞技到底好不好用

照片标题信用:“棕色chromis.学校在他们的水下环境中安全且稳定地走向不可变的收集未来“,”由Conor Myhrvold,博伊尔加勒比荷兰

注释