Yelp采用MVI架构的效果与优势
Yelp工程师Paul Martin表示,在Android应用中采用Model-View-Intent(MVI)架构四年后,该架构帮助他们实现了高性能的屏幕渲染,并显著提升了单元测试的效率。
采用MVI的背景
在此之前,Yelp使用的是Model-View-Presenter(MVP)架构。随着应用页面复杂度的增加,MVP架构的缺点逐渐显现,主要表现为Presenter文件的代码量过大,难以维护,尤其是在需要添加更多状态管理和复杂逻辑时。
MVVM架构的评估
Yelp工程师也曾考虑切换到Model-View-ViewModel(MVVM)架构,该架构更适合事件驱动和反应式UI。虽然MVVM在一定程度上缓解了MVP的问题,但随着视图复杂度的增加,数据类仍然会变得庞大且属性繁多。此外,MVVM与Yelp自有的Bento框架兼容性较差,这也是他们最终放弃MVVM的原因。
MVI架构的核心概念
MVI架构的核心是“意图”(Intent),它代表了用户通过UI触发的事件背后的意图。用户意图被转换为动作(Action),进而更新视图状态(View State),最终由视图渲染到屏幕上。MVI通过一个反应式流(reactive stream)来管理事件和状态的变化,模型和视图都订阅这个流以获取更新。
MVI的挑战与解决方案
MVI的一个弱点是事件与动作之间的映射通常通过大型switch语句实现,这限制了其扩展性。Yelp工程师通过注解机制解决了这一问题,将事件与对应的动作方法进行关联。例如:
@Event(HeaderClicked::class)
fun onHeaderClick() {
// do something
}在运行时,注解会被处理以创建事件与动作之间的映射。
代码复杂度的管理
为了应对视图复杂度增加导致的代码膨胀,Yelp引入了“子Presenter”(Sub-Presenter)的概念,类似于子模型。通过将动作模块化,子Presenter可以将不同部分的逻辑分离到不同的模块中,从而减少单一模型的复杂度。
单元测试的简化
由于事件和状态通过流(eventBus)传输,任何感兴趣的模块都可以观察这些变化,这种设计简化了单元测试。Yelp工程师通过presenter rules记录事件和状态,并使用断言来验证预期结果,例如:
fun whenButtonClicked_loadingProgressShown() {
presenterRule.sendEvent(ButtonClicked)
presenterRule.assertEquals { listOf(ShowLoadingProgress) }
}性能提升
采用MVI后,Yelp工程师能够将许多操作移至后台线程,从而提升了应用性能。具体效果包括:
- 平均帧渲染时间减少了50%以上。
- 冻结帧数减少了近4%。
这些改进显著提升了用户注册和登录等关键场景的体验。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。