Yelp 采用 MVI 架构以提高其 Android 应用的性能和可测试性

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%。
    这些改进显著提升了用户注册和登录等关键场景的体验。
阅读 25
0 条评论