当我们重写了Uber IOS和Android Rider应用程序在2016年,我们将应用程序细分为深层等级依赖注入范围。这允许在不了解彼此的情况下编写更多的特性,并减少了应用程序中陈旧状态的数量,从而提高了工程速度并促进了发展。
虽然iOS框架具有始终支持复合模式的ViewControllers,AOSP(Android开源项目)框架传统上没有支持深度嵌套的控制器或范围。因此,使用深度范围层次结构编写Android应用程序是困难和罕见的。优步的骑手应用程序是如何难以克服解决结构挑战的榜样。
Uber打车应用的用户体验包含共享地图等公共对象的状态;例如,主屏幕视图、产品选择视图和机场终端视图,如图1所示:
不同屏幕之间共享对象的存在意味着应用程序不能由一组不同的活动(例如,HomeActivity, AirportActivity和ProductSelectionActivity)组成,除非共享对象在全局范围内作为单例存储。为了解决这个问题,需要一种模式来控制如何在屏幕和子屏幕之间共享对象。
简而言之,我们需要一个有效的范围模式来支持新的Android Uber打车应用程序。
那么,我们是怎么做到的呢?在本文中,我们将讨论:
- 我们在旧骑手应用程序中使用的浅层范围模式及其问题。
- 我们在重写的附加程序中使用的深度范围模式及其改进。
- 不同的架构框架以及它们如何支持深层范围层次结构。
旧的优步骑手应用程序:一个两级范围层次
到2016年,我们显然已经超越了现有的Uber打车应用,因为它已经无法跟上我们维持和发展业务所需的规模和速度。
大多数旧的rider应用程序都包含在一个名为LoggedInActivity的活动中,它的行为就像一个具有单一作用域的控制器。LoggedInActivity中有多个不同的屏幕,它们执行截然不同的行为。因此,LoggedInActivity由一层处理不同UI和业务逻辑的子控制器类组成。其中一些子控制器如图3所示:
所有子控制器(如ConguteController),PricingController和AirportController等于同一LoggedInsCope,并且可以在所有子控制器之间共享所有依赖项注入对象。参见下面的dagger片段:
@LoggedInScope
@Component(modules = LoggedInActivityModule.class, dependencies = AppComponent.class)
公共接口LoggedInComponent {
空注入(LoggedInActivity活动);
}
考虑AirportController这样的控制器(参见图1中的中间屏幕):这个AirportController在LoggedInActivity的整个过程中都存在于内存中。这种情况有几个缺点:
-
- 耦合:其他控制器,比如communtecontroller,可以从AirportController的实用程序对象中读写,因为它们共享一个作用域。这不可避免地导致不相关的控制器和功能之间的耦合。
- 陈旧状态:AirportController完成在屏幕上显示自身显示在内存中,AirportController使用的所有实用程序对象都存在于内存中。当机场电源控制器被隐藏时,这导致工程师为AirthorController的实用程序对象编写错误的重置逻辑,然后在屏幕上再次显示。
- 国家组织学:由于对象和控制器持续到整个LoggedInsCope的持续时间内,因此需要了解它们在每个LoggedIn子句期间应该如何行事。这需要工程师用更容易出错的状态条件逻辑编写更大的类。
- 很难更新和测试:添加新的子变量器时,工程师需要考虑数十种控制器,例如机场连接器和实用类应该在这种新状态下表现。优步骑手应用程序具有令人震惊的大量功能,因为在数百个不同的城市中运行具有不同的限制和优化,因此两个级别的范围层次迅速变得无法管理。
三级范围层次结构更好吗?
由于有两层作用域,旧的rider应用程序是作用域层次太浅时会发生的一个极端例子。不幸的是,通过赋予每个控制器自己的作用域来创建额外的作用域层并不能解决上面列出的大多数问题。
对象通常需要在两个或三个控制器之间共享。由于只有三层作用域,这些共享对象需要存储在LoggedInScope中。随着时间的推移,第三个作用域层变得“薄”,因为许多对象被重构到第二层作用域。
显然,添加第三层作用域是一种改进。但这仍然会导致“胖”的第二层作用域,其中存在许多相同的问题。

新的骑士应用:深度范围层次
鉴于两个和三个级别的范围层次结构有重大问题,我们在开发新应用时,我们并没有将自己限制在一个集合的范围层。相反,我们在任何有用的地方创建了新的中间范围层。例如,PRERIQUEST范围用于存储需要由所有先决条件所需的对象(如家庭,产品),更精制的屏幕状态共享。
这种模式产生了作用域的深层层次结构(参见图4),提供了两个高级好处:
- 作用域树中的兄弟之间不共享任何数据或视图。需要共享的对象存储在中间节点中,因此叶子作用域被很好地封装了。
- 由于在机场门选择、位置优化和产品选择等范围之间不共享任何内部数据,所以在机场门选择流完成后,不需要在内存中存储任何机场数据。作为一个结果,新应用的控制器层次结构可以1:1映射到它的范围层次结构。
当我们将应用程序细分为一组寿命较短的小范围时,旧的rider应用程序中由两级范围层次结构引起的问题就消失了(如图4所示)。考虑一下这个新的作用域和层次结构是如何影响Airport特性的,如下所示:
- 低耦合:机场逻辑无法访问兄弟姐妹或堂兄范围的任何内存。因此,特征的发展可以独立于家庭,产品选择,机场和位置细化中完成。
- 更少的国家:只有在执行逻辑时,机场选择作用域才在内存中。因此,在隐藏机场UI时,不需要编写容易出错的复位逻辑。
- 更少的状态组合:大多数对象不再在LoggedIn作用域的整个持续时间内生存。例如,当处于Airport Selection状态时,Product Selection逻辑不需要做出关于其行为的任何决策,因为在机场门选择期间,内存中不存在Product Selection逻辑。
- 更易于更新和测试:在应用程序中添加新的替换机时,由于缺乏状态组合学,工程师不需要测试其对现有功能的影响。
常见的体系结构框架
有许多不同的方法可以创建深度范围层次结构,所以在确定一个之前,我们必须评估所有的选项。我们讨论了在我们决定重写附加应用程序之前考虑的架构肋骨我们的内部架构框架如下:
MVC和毒蛇
从旧骑手应用程序继承的CodeBase工程遵循MVC(模型 - 视图 - 控制器)模式。常见的教科书模式像MVC或Viper一样足够普遍能够支持深度嵌套的作用域层次结构,但控制器层次结构通常由视图嵌套决定。这对于深度作用域层次结构来说很不方便,因为许多作用域不创建任何视图。
所以我们没有使用mvc或viper。
流的应用
流主要是为了支持多层嵌套作用域而设计的。因为你可以创建除了共享对象(例如LoggedInScope)以外什么都不包含的无视图范围,所以Flow是一个强大的选择。但其他因素(游戏邦注:如缺少相应的iOS框架)阻止我们使用它。
所以我们没有“随大流”。
导线的应用
这样的框架导体不要显式支持作用域或嵌套作用域。你可以添加范围到每个控制器,如果你愿意克服:
- 没有DI模式的强制执行:如果您要使用大量的范围,这很重要。
- 冗余的观点: Conductor强制每个控制器创建一个视图,在使用深度嵌套的范围层次结构时导致冗余视图。
鉴于这些约束,我们抵制选择导体。
勺应用
其他应用程序也包含屏幕之间的共享视图对象和业务数据。例如,独家新闻基于流程的早期版本,以正式化控制器模式,该控制器模式可以在不创建全局状态的情况下共享映射等视图。
Scoop框架强烈强调范围。使用Scoop,作用域与导航堆栈相关联:在导航堆栈中越深入,就会在当前作用域下面嵌套一个作用域。例如,当从HomeController转换到ConfirmationController时,可以通过让ConfirmationController访问HomeController的范围来共享它们之间的对象。
Scoop的设计提供了方便的导航和动画模式,但代价是鼓励从控制器到控制器以及从控制器到活动的更大耦合,这是我们决心要避免的模式。
所以我们没有挖掘这个选项。
晚餐吃什么:排骨
由于我们之前考虑的选项都不符合Uber的要求,所以我们为我们的新乘客应用创建了自己的架构框架:肋骨,我们在它首次亮相后不久就详细介绍了它。与Scoop不同的是,导航堆栈和作用域是解耦的,并且Home和Confirmation之间唯一的共享对象存在于中间的先决作用域中。有了肋骨,为我们的rider应用程序使用嵌套的作用域模式很容易,因为有两个设计决策:
-
-
- 为肋条产生范围水板:我们创建了一个内部IntelliJ插件,生成RIB和Dagger 2组件/子组件样板无论何时工程师创造一个新的肋骨。因此,嵌套作用域是一种低摩擦的规范。
- 范围不需要耦合到视图:由于深度嵌套的作用域/控制器层次结构,这个功能是有用的。许多叶子作用域将希望从中间作用域修改视图,而不是创建它们自己的视图,而且许多中间作用域将只包含业务逻辑,而不是视图。
-
深刻的范围ftw!
深入的作用域层次结构使得像我们的rider应用这样的应用程序(具有功能密集的屏幕和子屏幕之间的共享对象)能够增加关注点的分离,减少陈旧数据的可能性,并提高开发速度。
一旦App包含具有高度解耦控制器的深度范围层次结构,就会更容易地添加强大的技术,例如静态分析以检测存储器泄漏,基于插件的编程和各种性能优化。
如果这种工作让你兴奋,那就参与进来,改善每个人的优步乘客体验iOS和安卓工程。与RIBS及以后有很多更多,我们将在2017年剩余时间内写入Uber应用程序的开发体系结构。





