最近准备去学一下新出的MVI架构,看了半天后才发现目前主流的MVI都是用kotlin语言写...去查阅kotlin的基础语法,然后发现还用到了携程等用法...去查阅携程用法,终于...现在再回头来看才终于看懂了些。
1、什么是MVI
MVI是继MVC、MVP、MVVM之后又出的一种架构,(Model-View-Intent)
- Model: 主要指UI状态(State)。例如页面加载状态、控件位置等都是一种UI状态
- View: 可能是一个Activity或者任意UI承载单元。MVI中的View通过订阅Model的变化实现界面刷新
- Intent: 此Intent不是Activity的Intent,用户的任何操作都被包装成Intent后发送给Model层进行数据请求
1、基于kotlin的架构
MVI架构中,将整个页面的状态存放于单一的类中,而且这个类必须是Kotlin的data class,kotlin的这个特殊的类自带了copy功能,非常方便去更新部分的属性。
目前android主流的MVI是基于协程+flow+viewModel去实现的,
就是把所有更新ui的方法转为intent,在viewModel中使用flow监听,
然后把界面数据状态转为state,在view中使用flow监听
view中使用时发送intent,viewModel监听到操作,修改对应的state,然后view监听到操作,更新ui(界面变化是数据流的末端,界面消费上游产生的数据,并随上游数据的变化进行刷新。数据永远在一个环形结构中单向流动,不能反向流动,而且能改变ui的数据只有state)
2、为什么要用MVI
优点
- UI的所有变化来自State,所以只需聚焦State,架构更简单、易于调试
- 数据单向流动,很容易对状态变化进行跟踪和回溯
- state实例都是不可变的,确保线程安全
- UI只是反应State的变化,没有额外逻辑,可以被轻松替换或复用
缺点
- 所有的操作最终都会转换成State,所以当复杂页面的State容易膨胀
- state是不变的,每当state需要更新时都要创建新对象替代老对象,这会带来一定内存开销
- 有些事件类的UI变化不适合用state描述,例如弹出一个toast或者snackbar
3、MVI在代码中怎们使用
想要在代码中使用MVI需要知道kotlin携程flow和携程作用域的用法
1、kotlin携程
协程是一种「并发设计模式」。作用是简化异步执行的代码。它可以帮助管理一些耗时的任务,以防耗时任务阻塞主线程。协程可以用同步的方式写出异步代码,代替了传统的回调方式,让代码更具有可读性。
优点
- 轻量:可在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作;
- 内存泄露更少:使用结构化并发机制在一个作用域内执行多个操作;
- 内置取消支持:取消功能会自动通过正在运行的协程层次结构传播;
- Jetpack集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。
//使用协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
2、Flow
Flow 是 Kotlin 官方基于协程构建的用于响应式编程的API。响应式编程简单来说就是使用异步数据流进行编程 。协程中,使用挂起函数仅可以异步返回单个值,而 Flow 则可以异步返回多个值,并补全kotlin语言中响应式编程的空白。
监听方法
userIntent.consumeAsFlow().collect {//监听 userIntent 中的方法
when (it) {//监测到 FetchUser被触发
is MainIntent.FetchUser -> fetchUser()
}
}
触发方法
lifecycleScope.launch {//携程作用域
viewModel.userIntent.send(MainIntent.FetchUser)
}
3、携程作用域
lifecycleScope 和viewModelScope
* lifecycleScope.launch{} 只能在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期 * 协程内的阻塞不会影响协程外 * 协程体是异步执行的,但是可以在其中做UI操作。线程也是异步的,但是不能更新UI,线程需要先切换到主线程。 * 默认情况下协程中的内容是串行的 * 如果需要并行,例如请求多个接口拿到数据后才能进行操作,可以使用 async{} * 手动停止的方法 job?.cancel() * 会绑定调用者的生命周期,因此通常情况下不需要手动去停止viewModelScope.launch{} 只能在ViewModel中使用,绑定ViewModel的生命周期
为什么要用:
当启动多个协程时,无论是追踪协程状态,还是单独取消各个协程,都是件让人头疼的事情。 * 不过,我们可以通过直接取消协程启动所涉及的整个作用域 (scope) 来解决这个问题,因为这样可以取消所有已创建的子协程。
//viewModelScope
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
//lifecycleScope
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
在代码中的用法
lifecycleScope.launch { //携程作用域
...
}
viewModelScope.launch {//携程作用域 能自动管理生命周期,自动取消请求
...
}
其他方法这里就不演示了,想要了解的自行百度下吧,东西挺多的