MVI 的概念
官网解释:
https://developer.android.google.cn/topic/architecture?hl=zh-cn
-
MVI在架构分层上和MVP没有本质区别,但区别主要体现在架构风格和编程思想上。
-
MVI 是 Model-View-Intent 的缩写,它也是一种响应式 + 流式处理思想的架构。
-
MVI 的 Model 代表一种可订阅的状态模型的概念,添加了 Intent 概念来代表用户行为,采用单向数据流来控制数据流动和各层依赖关系。
-
在代码的层面上它分为三层:
UI Layer(界面层) :在屏幕上展示应用界面和数据
Domian Layer(网域层) :封装复杂或可复用的业务逻辑
Data Layer(数据层) :获取可公开应用数据和完成业务逻辑 -
MVI 中的单项数据流工作流程如下:
用户操作以 Intent 的形式通知 Model
Model 基于 Intent 更新 State
View 接收到 State 变化刷新 UI
MVI - Model-View-Intent
- MVI是一种响应式和流式的处理思想,将意图事件(用户操作),通过函数转换为特定Model(状态),将其结果反馈给用户(渲染界面)。
- 抽象下来得到intent(),model(),view() 三个方法
- intent() 即意图,接收用户的输入(即UI事件,如点击事件)把意图和相关参数封装为数据结构,传递给model()方法。传递意图的接口是统一的,而不像MVP持有Presenter,并调用Presenter的多个接口。
- model() 不同于一般的model,这里说的model相当于状态模型里的一种状态,model内部的逻辑是不可变的,这能保证状态逻辑的一致性,同时降低系统的复杂性。
- view() 接收model传来的state渲染UI
代码案例
后台接口
/**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: 网络后台接口
*/
interface YaoServiceApi {
@GET("/banner/json")
suspend fun getBannerList() : ResponseEntity<List<BannerEntity>>
}
服务器返回类
**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: 服务器返回的封装类
*/
data class ResponseEntity<T>(val errorCode:Int,val data:T,val errorMsg:String)
具体的类
/**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: TODO
*/
data class BannerEntity(
val desc: String,
val id: Int,
val imagePath: String,
val isVisible: Int,
val order: Int,
val title: String,
val type: Int,
val url: String
)
网络工具类
object RetrofitFactory {
private val retrofit: Retrofit
//http://43.143.146.165:7777/banner/json
init {
retrofit=Retrofit.Builder()
.baseUrl("http://43.143.146.165:7777/")
.addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(CoroutineCallAdapterFactory.create)
.client(createOkHttpClient())
.build()
}
private fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
// .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
// .readTimeout(TIMEOUT, TimeUnit.SECONDS)
// .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
// .addNetworkInterceptor(createLoggingInterceptor())
.addInterceptor(createLoggingInterceptor())
// .addInterceptor(createTokenInterceptor())
// .addInterceptor(createNormalErrorHttpCodeInterceptor())
.build()
}
private fun createLoggingInterceptor(): Interceptor {
return HttpLoggingInterceptor().apply { level=HttpLoggingInterceptor.Level.BODY }
}
/**
* create api instance
*/
fun <T> create(clazz: Class<T>) : T = retrofit.create(clazz)
仓库类
/**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: 数据层的类
*/
class BannerRepostory @Inject constructor() {
suspend fun getBannerList(): ResponseEntity<List<BannerEntity>>{
return RetrofitFactory.create(YaoServiceApi::class.java).getBannerList()
}
}
意图类
/**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: 意图类,也就是用户要干啥
*/
sealed class BannerIntent {
object GetBannerList : BannerIntent()
//举例,可以放多种不的请求,
// data class GetBannerTitleList(val a:Int) :BannerIntent()
}
状态类
/**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: 状态类,主要对用户的请求的意图是否成功.
* sealed 是密封类. object是单例,不需要参数 dataclass 构造要参数
*/
sealed class BannerUIState {
//成功
data class Success(var banner: ResponseEntity<List<BannerEntity>>): BannerUIState()
//失败
data class Fail(var fail:ResponseEntity<List<BannerEntity>>) : BannerUIState()
//加载中
object loading: BannerUIState()
object Init:BannerUIState()
}
viewModel类
@HiltViewModel
/**
* 时间:2023/5/19
* @author Mr.Feng
* 简述: vm类
*/
class BannerViewModel @Inject constructor(private val repostory: BannerRepostory) : ViewModel(){
val channel = Channel<BannerIntent>(Channel.UNLIMITED)
//不让外部调用
private val _state = MutableStateFlow<BannerUIState>(BannerUIState.Init)
//使用flow来监听
val state: StateFlow<BannerUIState>
get() = _state
init {
handleIntent()
}
//准备处理所有意图
private fun handleIntent() {
//起协程准备处理
viewModelScope.launch {
channel.consumeAsFlow().collect{
when(it){
is BannerIntent.GetBannerList -> getBannerList()
}
}
}
}
private fun getBannerList() {
viewModelScope.launch {
val bannerList = repostory.getBannerList()
if(bannerList.errorCode == 200){
_state.value = BannerUIState.Success(bannerList)
}else{
_state.value = BannerUIState.Fail(bannerList)
}
}
}
// private fun bannerList() : Flow<ResponseEntity<List<BannerEntity>>> = flow {
// //发送网络请求
// val bannerList1 = repostroy.getBannerList()
// emit(bannerList1)
// }
}
页面类
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val veiwmodel: BannerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch{
veiwmodel.channel.send(BannerIntent.GetBannerList)
}
lifecycleScope.launch {
veiwmodel.state.collect{
when(it){
is BannerUIState.Success -> {
println("成功")
println(it.banner.data.size)
}
is BannerUIState.Fail -> {
println("失败")
}
else -> {
println("其他")
}
}
}
}
}
}
启动类
/**
* 时间:2023/5/20
* @author Mr.Feng
* 简述: TODO
*/
@HiltAndroidApp
class MyApp : Application() {
}