Android-MVX技术总结
1. MVC (Model-View-Controller)
本质与作用:
MVC 是最经典的架构模式,它的核心思想是职责分离。Model 负责数据和业务逻辑,View 负责 UI 展示,Controller 负责处理用户输入并协调 View 和 Model 的交互。
转换的意义:
它将业务逻辑从 UI 界面中剥离出来,避免了所有代码都堆砌在 View(如 Android 中的 Activity)中。但在实际应用中,尤其是移动端开发,Controller 经常变得臃肿,它既要处理 UI 事件,又要手动更新 UI,导致 View 和 Controller 之间存在紧密耦合。Controller 既是“指挥官”,又是“搬运工”,承担了过多的责任。
2. MVP (Model-View-Presenter)
本质与作用:
为了解决 MVC 中 View 和 Controller 的紧密耦合,MVP 应运而生。它引入了 Presenter,作为 View 和 Model 之间的“中间人”。View 变成了被动的接口,Presenter 不直接持有 View 实例,而是通过 View 接口来与 View 通信。Presenter 负责处理所有的业务逻辑,并手动调用 View 接口方法来更新 UI。
转换的意义:
MVP 的最大意义在于彻底解耦了 View 和 Presenter。Presenter 不依赖于具体的 Android 组件(如 Activity、Fragment),因此可以独立进行单元测试。这使得业务逻辑的可测试性大大提高。然而,这种模式的问题在于 Presenter 仍然需要手动调用 view.showData() 或 view.updateUI() 等方法来更新 UI,这种命令式的更新方式依然繁琐,而且 Presenter 和 View 之间形成一对一的强依赖,导致 Presenter 的生命周期管理变得复杂,容易引发内存泄漏(需要手动调用 detachView())。
3. MVVM (Model-View-ViewModel)
本质与作用:
MVVM 的出现,是为了解决 MVP 中命令式 UI 更新的痛点。它引入了 ViewModel,并结合数据绑定(Data Binding)技术。ViewModel 封装了 UI 状态和数据,并通过 LiveData 或 StateFlow 等可观察对象暴露给 View。View 不再需要手动调用方法更新自己,而是声明式地将 UI 组件与 ViewModel 中的数据绑定起来。当 ViewModel 中的数据改变时,View 会自动刷新。
转换的意义:
这是从“命令式编程”到“声明式编程”的范式升级。开发者从繁琐的 UI 更新操作中解放出来,可以更专注于业务逻辑的实现。ViewModel 独立于 Android 组件的生命周期,可以安全地保存数据,解决了屏幕旋转等配置变更导致的数据丢失问题,并且不需要手动管理内存泄漏。MVVM 让代码更精简、更易于维护和测试。
4. MVI (Model-View-Intent / Model-View-Interpreter)
本质与作用:
MVI 的核心思想是单向数据流(Unidirectional Data Flow)和不可变状态(Immutable State)。它将用户操作封装为“意图(Intent)”,意图传递给“模型(Model)”,模型处理意图后产生一个新的、不可变的“状态(State)”,View 只是被动地根据这个最新的状态来渲染 UI。整个数据流是 View → Intent → Model → State → View 的闭环,清晰且可预测。
转换的意义:
MVI 进一步强化了可预测性和可调试性。由于整个应用的状态由一个单一的、不可变的对象来管理,开发者可以非常清晰地追踪状态的每一次变化。这在处理复杂、多变的用户交互和状态管理时非常有用。例如,在调试时,你可以回溯所有的历史状态来重现问题。MVI 模式特别适合那些业务状态复杂的应用,如电商购物车、社交动态流等,它有效地避免了状态混乱和“数据交叉感染”的问题。它代表了当前函数式编程思想在移动端 UI 架构中的实践。
5.为什么使用MVVM
首先,传统的 MVC 模式因为其耦合度高,导致单元测试编写困难;而 MVP 模式虽然在一定程度上降低了耦合度,但在接口粒度控制上仍存在挑战。这两种模式都未能完全满足公司对架构设计的要求。
同时,尽管 MVI 模式被认为能够解决上述问题,但由于其在公司内部是全新的技术栈,团队成员需要投入大量的学习成本,这与项目紧迫的交付周期相冲突,因此MVI模式暂时被排除。
综合以上考量,MVVM 模式成为了最优解。它通过支持 View 和 ViewModel 之间的双向数据绑定,使视图层变得完全无状态化,大大简化了开发流程。更重要的是,公司团队对 MVVM 模式有着丰富的经验,这能够显著减少学习和开发成本,确保项目按时交付。
此外,在具体的技术栈实施上,实习中采取了分阶段策略,以兼顾效率与技术更新:现有基于 Java 的成熟模块将被复用,以加速交付周期;而所有新功能将使用 Kotlin 进行开发。这一选择得益于 Android 官方的 Jetpack 组件对 Kotlin 的良好支持,这一组合不仅能提升开发效率,还能有效减少潜在的 bug,从而确保新功能的稳定性和质量。
6.MVVM+jetpack结合
数据驱动 UI 的工作流程
UI 获得 ViewModel 实例:工作流程始于 View(通常是 Activity
或 Fragment
)获取一个 ViewModel
的实例。View 并不直接持有或管理数据,它只知道如何从 ViewModel
获取数据。
这个过程并非直接通过 new ViewModel()
完成,而是通过 ViewModelProvider
类实现。在底层,ViewModelProvider
会检查一个名为 ViewModelStore
的内部容器,来查找是否已有目标 ViewModel
的实例。如果存在,它会直接返回这个已有的实例,确保在配置变更(如屏幕旋转)时数据不会丢失。如果不存在,ViewModelProvider
则会调用其内部的 Factory
来创建新的 ViewModel
实例,并将其存入 ViewModelStore
中,供后续使用。
ViewModel 持有 LiveData:ViewModel
负责从数据源(例如网络或数据库)获取数据,并将数据包装在 LiveData
对象中。LiveData
是一个可观察的数据持有者,这是实现数据驱动 UI 的关键。
LiveData
的核心是其生命周期感知能力。当你调用 liveData.observe(lifecycleOwner, observer)
方法时,LiveData
会在内部将传入的 observer
与 lifecycleOwner
的生命周期状态绑定。当 lifecycleOwner
的生命周期变为 STARTED
或 RESUMED
状态时,LiveData
的内部机制会将其标记为“活跃”;当其变为 STOPPED
或 DESTROYED
状态时,则标记为“非活跃”。这种机制保证了 LiveData
只会在 UI 处于安全状态时才分发数据,从根源上防止了因 UI 不可见而导致的内存泄漏和崩溃。
DataBinding 建立连接:DataBinding
在幕后工作,通过 XML 布局文件中的绑定表达式,为 UI 元素(如 TextView
)和 ViewModel
中的 LiveData
对象建立起连接。UI 元素现在“监听”着 LiveData
。
DataBinding
的连接工作是在编译时完成的。当你在 XML 布局文件中使用绑定表达式时,编译工具会自动生成一个名为 XXXBinding
的绑定类(例如 ActivityMainBinding
)。这个生成的类在内部持有对布局文件中所有 View 的引用,并包含一个 setViewModel()
方法来注入你的 ViewModel
实例。最关键的是,这个生成的类会通过调用 livedata.observe(lifecycleOwner, ...)
方法,在幕后为所有绑定的 LiveData
对象注册一个观察者。
数据变化并自动更新 UI:当 ViewModel
中的数据发生变化时,它会更新 LiveData
对象的值。LiveData
会自动通知所有活跃的观察者(也就是 DataBinding
)。DataBinding
接收到通知后,会立即、无须人工干预地更新所有与其绑定的 UI 元素。
当 ViewModel
中的数据发生变化时(例如调用 myLiveData.setValue(newData)
),LiveData
的内部会通知其所有活跃的观察者。这个通知会传递给 DataBinding
生成的绑定类。在底层,这个绑定类会通过其观察者回调方法,根据数据变化来找到对应的 UI 元素(如一个 TextView
),然后直接调用其相应的 setter
方法(如 setText()
),将新数据设置到 UI 上。整个过程是完全自动化的,开发者无需编写任何手动更新 UI 的代码。
在 MVVM 架构中,数据与 View 的绑定交互是一个自动化且高效的过程。ViewModel 中的数据被封装在 LiveData 对象里。在编译阶段,DataBinding 会生成代码,将 View 和 LiveData 关联起来,从而使 View 成为 LiveData 的“观察者”。当 ViewModel 中的数据发生变化时,LiveData 会自动通知 DataBinding,然后 DataBinding 立即自动更新相应的 UI 元素。整个流程无需手动干预,实现了 UI 的被动更新,极大地简化了开发工作。