宁波小程序开发_宁波软件开发_宁波网络公司【昱远信息】 15058005455
一个App的架构需要处理任务:更新Model——如何处理两个

本文主要介绍MVC、MVP和MVVM架构。至于MVI,后面会单独介绍。这些MVX的目的是为了将业务和视图分离,松耦合,作为Android程序员,大部分都不陌生。

一个App离不开Model和View这两个角色。Model决定了App的数据,View决定了数据如何展示给用户。大多数框架或组件基本上都是用来处理两者之间的交互的。.

因此,一个 App 的架构需要处理两个任务:

更新模型 - 如何处理视图操作?更新视图 - 如何将模型的数据呈现给视图?

基于此,Android(不包括MVI)上一般有以下三种常见架构:

MVC - Model-View-Controller:Actvity/Fragment等作为Controller层充当View角色,代码过于臃肿;同时,很容易直接在View层操作Model,导致View与Model层耦合,无法独立复用。有时看到一个 Activity 可以有数千甚至数万行代码是一场噩梦。

MVP - Model-View-Presenter:Presenter 和 View 层之间的通信是通过定义接口来实现的,这将 View 和 Model 层解耦。但是当业务场景比较复杂的时候,接口定义会越来越多,定义可能比较模糊。一旦接口改变,相应的实现也需要改变。

MVVM - Model-View-ViewModel:MVVM解决了MVP的问题,让ViewModel和View不再依赖接口通信,而是通过LiveData、RxJava、Flow等响应式开发方式进行通信。

我们可以看看这里对Model和View的理解:

View:视图,呈现给用户的界面,直接与用户交互的一层。

模型:模型通常应该包括数据和一些业务逻辑,即数据的结构定义,以及存储和检索。对于外部组件,模型通常代表提供给它们的数据。毕竟,他们不在乎数据从哪里来小程序定制案例,到哪里去,他们只关心自己。

MVC

定制微信小程序价格_小程序定制案例_上饶小程序定制

该架构涉及三个角色:模型-视图-控制器。Controller是Model和View之间的桥梁,用来控制程序的流程。

记得在网上看了很多MVC的文章,但是好像有些文章给出的模型图不太一样,一时间有点疑惑。其实不同的是,MVC模型开发后有变种。MVC 的一个版本如下所示:

Android MVC、MVP、MVVM 架构案例解析

这个版本的一般交互流程是:

用户对View进行操作,例如产生点击事件。控制器接收事件并对它们做出反应。比如点击登录事件,会检查用户输入是否为空,如果为空则直接返回View提示用户;如果不为空,它将请求模型层。Model处理完毕后,需要将登录用户的数据通知给相关成员,也就是上图中的View层。View 收到后会做相关显示。

上图中,View层依赖于Model层,降低了View的复用性。对于解耦,出现以下版本:

Android MVC、MVP、MVVM 架构案例解析

这个版本的主要变化是 View 和 Model 不直接通信。View 通过 Controller 更新 Model 层的数据。Model层完成逻辑后,通知Controller层,Controller更新View。

MVC 架构总结

MVC 为视图和业务的分离提供了开创性的思路,解耦了 View 和 Model 层,提高了复用性。

但是,在Android的实际应用中,很容易出现一个新的角色——ViewController。比如Activity作为View和Controller,非常臃肿,耦合变得严重,不方便进行单元测试。

MVP

小程序定制案例_定制微信小程序价格_上饶小程序定制

该架构涉及三个角色:Model-View-Presenter。关系图如下:

Android MVC、MVP、MVVM 架构案例解析

这张图和上面第二个版本的MVC结构非常相似。不同的是Controller被Presenter层代替,职责类似,但实现方式不同。MVP通过接口进行通信,三层都有自己的接口来定义自己的行为和能力,可以减少耦合,提高可重用性,方便单元测试。

交互过程还是如下:用户操作View层,产生事件;Presenter 接收事件,响应它,并请求 Model 层;Model层处理它并通知给Presenter,然后Presenter通知View层。

通过登录场景举个栗子

1、先定义各层的接口,一起写一个场景的接口。

interface ILogin {
    interface ILoginView {
        fun loginLoading() // 登陆中
        fun loginResult(result: Boolean) // 登陆结果
        fun isAvailable(): Boolean // IView 是否可用
    }
    interface ILoginPresenter {
        fun attachView(view: ILoginView) // attach View
        fun detachView() // detach View, 防止内存泄漏
        fun isViewAvailable(): Boolean
        fun login()
    }
    interface ILoginModel {
        fun login(listener: OnLoginListener)
    }
    interface OnLoginListener {
        fun result(result: Boolean)
    }
}

2、查看层实现。

class MVPLoginActivity : AppCompatActivity(), ILogin.ILoginView {
    private val loginPresenter = LoginPresenter()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(Button(this).apply {
            text = "登录"
            setOnClickListener {
                loginPresenter.login()
            }
        })
        loginPresenter.attachView(this)
    }
    override fun loginLoading() {
        Toast.makeText(this, "Login...", Toast.LENGTH_SHORT).show()
    }
    override fun loginResult(result: Boolean) {
        Toast.makeText(this, "Login result: $result", Toast.LENGTH_SHORT).show()
    }
    override fun isAvailable() = !isDestroyed && !isFinishing
    override fun onDestroy() {
        super.onDestroy()
        loginPresenter.detachView()
    }
}

3、Presenter 层实现。

class LoginPresenter : ILogin.ILoginPresenter, ILogin.OnLoginListener {
    private val loginModel: ILogin.ILoginModel = LoginModel()
    private var loginView: ILogin.ILoginView? = null
    override fun attachView(view: ILogin.ILoginView) {
        loginView = view
    }
    override fun detachView() {
        loginView = null
    }
    override fun isViewAvailable(): Boolean = loginView?.isAvailable() ?: false
    override fun login() {
        loginView?.loginLoading()
        loginModel.login(this)
    }
    override fun result(result: Boolean) {
        if (isViewAvailable()) {
            loginView?.loginResult(result)
        }
    }
}

4、模型层实现。

定制微信小程序价格_小程序定制案例_上饶小程序定制

class LoginModel : ILogin.ILoginModel {
    override fun login(listener: ILogin.OnLoginListener) {
        thread {
            Thread.sleep(1000)
            runOnUIThread {
                // 返回登录结果
                listener.result(Random.nextBoolean())
            }
        }
    }
}

以上只是一个例子。在实际开发中,一些基本的重复逻辑当然会被抽取到Base类中。

MVP 架构总结

MVVM

MVVM 模式

该架构涉及三个角色:Model-View-ViewModel。关系图如下:

Android MVC、MVP、MVVM 架构案例解析

它看起来类似于 MVP。不同的是Presenter被ViewModel代替,ViewModel负责与Model层交互,以可观察对象的形式向View提供数据。ViewModel 与 View 层分离,即 ViewModel 不应该知道 View 正在与什么交互。

如前所述,Model层包括一些业务逻辑和业务数据模型,而ViewModel层就是View Model(视图模型),它包含了视图的呈现数据和逻辑。比如Model层的业务数据是1、2、3、4,当翻译到View层时,可能代表A、B、C、D。ViewModel除了这样做之外,还封装了视图,例如单击控件后的行为。还要注意,Jetpack 包中提供的 ViewModel 和 ViewModel 组件不是一回事。这里的 ViewModel 是一个概念,Jetpack 包提供了更方便的实现。

很多关于MVVM的文章例子都会用到DataBinding,但是没有DataBinding,你仍然可以使用MVVM架构小程序定制案例,比如LiveData、RxJava、Flow等。这些工具都是基于响应式开发的原理来替代基于接口的通信方式. 在实际开发中,我从未见过DataBinding的使用。另外,如果真的要使用DataBinding,尽量避免在xml中写代码逻辑。相反,您应该用变量替换它来表示属性并在 Kotlin 代码中分配它。

这里的响应式开发强调一种基于观察者模式的开发方式:View订阅ViewModel暴露的响应式接口,并在收到通知后执行相应的逻辑,而ViewModel不再持有任何形式的View引用,减少耦合 小程序定制企业 ,提高复用性。

另外,如果使用LiveData,ViewModel只将LiveData接口暴露给View层,是不允许直接在View层更新LiveData的,因为一旦View层有了直接更新LiveData的能力,就无法约束View层进行业务处理:

小程序定制案例_定制微信小程序价格_上饶小程序定制

class LoginViewModel : ViewModel() {
    private val _loginResult: MutableLiveData = MutableLiveData()
    val loginResult: LiveData = _loginResult
}

关于流量小程序定制案例,我之前写过一篇关于掘金的学习笔记。如果您有兴趣,可以阅读:Kotlin Coroutines 中 Flow 的工作原理。

以登录结果为例,MVVM是基于LiveData的交互过程:首先,ViewModel中有一个LiveData属性来表示登录结果,它暴露的是LiveData而不是MutableLiveData,View层订阅了这个数据;View层点击登录后,调用VM的登录接口,VM请求Model层的登录能力;事件发生后Model通知VM 微信软件制作公司 ,VM更新MutableLiveData登录状态,View收到LiveData的变更通知,然后更新UI。

例子

1、Model层模拟登录并返回登录结果。

class LoginModel {
    // 模拟登录
    suspend fun login(): Boolean = withContext(Dispatchers.IO) {
        delay(1000)
        Random.nextBoolean()
    }
}

2、ViewModel层暴露了登录方法,并提供LiveData数据来表示登录状态 开发一款app得多少钱 ,允许View层订阅。

class LoginViewModel : ViewModel(), CoroutineScope by MainScope() {
    private val loginModel = LoginModel()
    private val _loginResult: MutableLiveData = MutableLiveData()
    val loginResult: LiveData = _loginResult
    fun login() {
        launch {
            _loginResult.value = 0
            val result = loginModel.login()
            _loginResult.value = if (result) 1 else -1
        }
    }
    // 模拟状态
    fun loginProgressText(result: Int): String = when (result) {
        0 -> "登录中"
        1 -> "登录成功"
        else -> "登录失败"
    }
}

3、View 层处理点击事件并订阅登录状态。

class MVVMLoginActivity : AppCompatActivity() {
    private val viewModel: LoginViewModel by lazy {
        ViewModelProvider(this).get(LoginViewModel::class.java)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(Button(this).apply {
            text = "登录"
            setOnClickListener {
                viewModel.login()
            }
        })
        // 监听登录状态
        viewModel.loginResult.observe(this, {
            Toast.makeText(
                this,
                "Login result: ${viewModel.loginProgressText(it)}",
                Toast.LENGTH_SHORT
            ).show()
        })
    }
}

存储库

存储库模式的概念来自领域驱动设计。主要思想是通过抽象一个Repository层,业务(领域)层屏蔽不同数据源的访问细节,业务层(可能是ViewModel)不需要关注具体的数据访问细节。

上饶小程序定制_小程序定制案例_定制微信小程序价格

Repository 在内部实现了对不同数据源(DataSource)的访问。典型的DataSource包括远程数据、Cache缓存、Database数据库等,可以用不同的Fetcher来实现。Repository 拥有多个 Fe​​tcher 引用。

因此,可以将上例中的 LoginModel 替换为 LoginRepository 类。LoginRepository 没有暴露具体的数据访问方式 小程序开发一般多少价格 ,只是暴露了这个能力的接口。

最后

建筑不是一蹴而就的。希望有朝一日,我们能从自己写的代码中找到架构的成就感,而不是几票就跑掉。这类文章要时时更新,记录学习在建筑道路上的足迹 定制小程序公司 ,一一揭开建筑的奥秘。

在这里,我还分享了一个学习PDF+架构视频+面试文档+源码笔记,进阶架构技术进阶脑图,Android开发面试题目资料,以及老大收集整理的进阶进阶架构资料。

这些是我在业余时间会一遍又一遍地阅读的好材料。对近年来大厂访谈的高频知识点进行了详细讲解。相信它可以有效地帮助你掌握知识,理解原理,帮助你在日后得到一个好的答案。

当然,你也可以用它来检查差距,提高你的竞争力。

真心希望对大家有所帮助,安卓任重而道远,大家互相鼓励!

需要的可以私信我【进阶】获取

Android MVC、MVP、MVVM 架构案例解析

Android MVC、MVP、MVVM 架构案例解析

Android MVC、MVP、MVVM 架构案例解析

返回列表

相关动态

<