概要
MVI(Model-View-Intent)框架是一种软件架构模式,尤其在移动应用开发中较为流行,它被设计来简化应用程序的复杂性和可维护性。MVI模式是对传统的MVVM(Model-View-ViewModel)模式的一种演进,强调了数据的单向流动和状态管理的重要性,这使得它在处理复杂的用户界面交互和状态变化时更加有效。
整体架构流程
技术名词解释
Model:
Model代表了应用程序的状态,通常是一个不可变的数据结构。
它可以包含业务逻辑或数据层的处理,但主要职责是存储和更新状态。
Model通常是响应Intent而产生新的状态。
View:
View负责展示Model中的数据,它是用户界面的表示。
View不直接与Model通信,而是通过Intent来表达用户的操作。
View通常会订阅Model的变化,并根据Model的状态更新UI。
Intent:
Intent是用户操作或者外部事件的抽象表示,比如按钮点击、网络请求完成等。
Intent触发Model的更新,从而导致View的重新渲染。
Intent可以看作是用户输入和系统输出之间的桥梁。
技术细节
帮助各位理解MVI
假设你正在玩一个电子游戏,游戏里有一个角色(可以把它想象成Model),一个屏幕(View),以及你的操作(Intent)。
Model(模型):这就像游戏里的角色,它包含了所有关于角色的信息,比如生命值、位置、装备等。每当游戏世界中发生一些事情,比如你打败了一个怪兽,Model就会更新,比如增加经验值或掉落物品。
View(视图):这是你看到的游戏画面,也就是屏幕上的显示。它不会自己决定要显示什么,而是根据Model里的信息来更新画面。比如,如果Model里的生命值减少了,View就会显示角色的生命条变短。
Intent(意图):这代表你的操作,比如按下的按键或触摸屏上的滑动。当你按下攻击键,这个动作就是Intent,它告诉游戏(Model)去做某些事情,比如让角色挥剑。
整体流程
1.封装BaseViewModel
open class BaseViewModel<I,S>(initState:S):ViewModel() {
val intent by lazy {
Channel<I>(Channel.UNLIMITED)
}
val state by lazy {
MutableStateFlow<S>(initState)
}
val service:ApiService by lazy {
service()
}
}
2.封装BaseView
@Suppress("UNCHECKED_CAST")
abstract class BaseView<B:ViewDataBinding,VM:BaseViewModel<*,*>>:AppCompatActivity() {
private val types by lazy {
(javaClass.genericSuperclass as ParameterizedType).actualTypeArguments
}
val binding:B by lazy {
val clazz = types[0] as Class<B>
clazz.getMethod("inflate", LayoutInflater::class.java).invoke(null,layoutInflater) as B
}
val viewModel:VM by lazy {
val clazz = types[1] as Class<VM>
ViewModelProvider(this)[clazz]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initData()
initView()
}
abstract fun initView()
abstract fun initData()
}
3.封装完成,实际应用,获取意图
interface LoginIntent {
data class login(val password:String,val username:String):LoginIntent
}
.4.获取状态
interface LoginState {
data class LoginSuccess(val user:Res<LoginEntity>):LoginState
data class LoginFail(val error:String):LoginState
data object default:LoginState
}
5.实际运用之ViewModel
class LoginViewModel :BaseViewModel<LoginIntent,LoginState>(initState = LoginState.default) {
init {
viewModelScope.launch {
intent.consumeAsFlow().collect{
when(it){
is LoginIntent.login->Login(it.password,it.username)
}
}
}
}
private fun Login(password: String, username: String) {
viewModelScope.launch(Dispatchers.IO) {
service.Login(password,username).collect{
if(it.code == 0){
state.value = LoginState.LoginSuccess(it)
}else{
state.value =(LoginState.LoginFail(it.msg))
}
}
}
}
}
这里的service是网络请求
6.实际应用之VIew
class LoginActivity : BaseView<ActivityLoginBinding, LoginViewModel>() {
val map by lazy {
mutableMapOf<String, String>()
}
override fun initView() {
binding.user = map
binding.btn.setOnClickListener {
viewModel.intent.trySend(
LoginIntent.login(
map["password"] ?: "",
map["username"] ?: ""
)
)
}
}
override fun initData() {
lifecycleScope.launch {
viewModel.state.collect {
when (it) {
is LoginState.LoginSuccess -> {
Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show()
ARouter.getInstance().build("/login/main").navigation()
}
is LoginState.LoginFail -> {
Toast.makeText(this@LoginActivity, "${it.error}", Toast.LENGTH_SHORT).show()
Log.d("+++", "${it.error}")
}
}
}
}
}
}
这里用的双向绑定
~~~到此结束~~~