您现在的位置是:首页 >学无止境 >浅浅的理解MVI网站首页学无止境

浅浅的理解MVI

馮贰爺 2024-06-17 11:19:17
简介浅浅的理解MVI

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() {
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。