因为前两年MVP架构非常火,于是我也将项目重构成了MVP的架构了,还写过一篇博客 从天地初开到MVC再到MVP 来记录如何从mvc过渡到mvp,以及他们的优缺点,感兴趣的可以看看。
目前mvp架构我也用了很长一段时间了,也专门结合kotlin、Retrofit、Rxjava、Dagger2等主流框架封装了一个mvp的基础架构,随着时间和项目的沉淀也越来越完善,但是Google更加推荐的是使用Android Jetpack来构建项目,最近手上的活不多,于是乎趁着空闲时间又把Google推荐的Jetpack架构看了一遍,并且又尝试着将mvp架构转成了mvvm架构。
关于如何搭建mvvm架构,首先建议大家看看官方文档Android Jetpack 中的架构(Architecture)组件。我们的MVVM架构实际上就是使用了架构组件中的部分组件。
文档中也说明了,Android Jetpack 组件是库的集合,这些库是为协同工作而构建的,不过也可以单独采用,同时利用 Kotlin 语言功能帮助您提高工作效率。可全部使用,也可混合搭配!
也就是说你可以根据自己的需求来选择使用Android Jetpack中部分组件来配合你现有的架构。
目前架构中有以下组件
结合我现有的需求我目前只使用了3个组件分别是
Lifecycles
作用:更方便的处理Android中生命周期的问题,它可以使你的组件具有感知生命周期的能力,从而根据生命周期状态来自动的响应一些动作
如何使用:https://developer.android.com/topic/libraries/architecture/lifecycle
ViewModel
作用: 用来管理数据,它同样具有感知生命周期的能力,在宿主没有被销毁之前,数据不会丢失,且ViewModel不会重新创建,比如旋转屏幕等。同时,ViewMedel将数据从Activity中抽离出去,耦合度更低,更加方便维护
如何使用:https://developer.android.com/topic/libraries/architecture/viewmodel
LiveData
配合ViewModel一起使用,存在于ViewModel中
LiveData 是一个可观测数据的容器类,与普通的可观测类不同,LiveData 能感知生命周期,并且只会在这些可观测的应用组件处于活动状态的时候才会更新它们,而且还会在与其关联的生命周期被销毁后自动清理自己。这样一来也就不会出现内存泄漏的问题了。
作用:底层数据改变时会自动更新UI,实际上我们可以看做是ViewModel于View之间通信的桥梁
如何使用:https://developer.android.com/topic/libraries/architecture/livedata
首先我们新建一个项目
添加lifecycle-extensions依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
目前来讲新建项目默认会使用AndroidX依赖库了,还没有迁移到AndroidX库的赶紧了。
下面是项目的结构,
data:存放数据实体类
model:存放业务类
view_model:存放ViewModel相关的类
对比之前mvp的项目结构我们会发现类文件会少很多,实际上主要是少了view接口这一层。
我们都知道,在mvp中,我们需要通过view接口来实现Presenter与View之间的通信。
那么在mvvm中,我们是通过LiveData来实现ViewModel与View层之间的通信的,而且这个通信不是手动的,其核心是通过数据驱动的,也就是数据发生变化,view层会感知到并自动刷新ui。
下面我们来看看mvvm结构下登录的实现代码
首先看实体类:
没什么好说的,跟之前得实体类一样
/*
* 实体类
* */
data class LoginBean(var code: Int, var message: String)
再来看LoginModel,一般来讲,为了减少ViewModel中的代码,我们会把一些业务相关的代码抽出去实现。
下面的就是模拟登录的逻辑,返回一个登录结果
/*处理登录逻辑*/
class LoginModel {
/*模拟请求接口返回的数据*/
fun login(): LoginBean {
return LoginBean(1,"登录成功")
}
}
ViewModel 和 LiveData
再来看看LoginVm ,前面我们也说了,ViewModel就是用来管理数据的,也就是说正常来讲我们的数据都是要放在ViewModel中来管理的,View层中是不应该出现存放数据相关的代码的。
LoginVm 中存储了loginBean ,loginBean 是LiveData类型的数据,当他发生改变时,就是被View感知到。
/*
* LoginViewModel
*
* 管理登录的数据以及处理登录相关逻辑
* */
class LoginVm : ViewModel() {
private var loginModel = LoginModel()
/*LiveData,使该数据可以被感知*/
var loginBean = MutableLiveData()
var account: String = ""
var pwd: String = ""
fun doLogin() {
Handler().postDelayed(object : Runnable {
override fun run() {
/*通知Activity刷新数据*/
loginBean.value = loginModel.login()
}
}, 3000)
}
}
View层
我们在View层
/*
* View层
*
* */
class MainActivity : AppCompatActivity() {
private lateinit var vm: LoginVm
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
vm = ViewModelProviders.of(this).get(LoginVm::class.java)
btn_login.setOnClickListener {
vm.account = et_user.text.toString().trim()
vm.pwd = et_pwd.text.toString().trim()
vm.doLogin()
}
vm.loginBean.observe(this, object : Observer {
override fun onChanged(t: LoginBean) {
/*数据发生变化*/
Toast.makeText(this@MainActivity, t.message, Toast.LENGTH_LONG).show()
}
})
}
}
这样一来,一个最简单的mvvm结构的登录示例就好了。
下面我们来运行看下效果。
可以看到,点击登录按钮3秒后,view层已经正确的显示Toast了。
耦合度更低,复用性更强,没有内存泄漏
虽然mvp中Presenter没有直接持有View层的实例,但是还是持有了View层的接口,在开发过程中我们还需要处理生命周期相关的问题,一旦处理不好,就会造成内存泄漏,例如Activity已经销毁了,你还在通知view去更新ui,这样跟容易造成内存泄漏。
而在mvvm中,ViewModel是不会持有任何View层引用的,也就是说ViewModel是完全跟View层分离的,耦合度更低,且更容易复用代码,配合LiveData后我们无需处理生命周期相关的逻辑,有效的避免了内存泄漏问题
类文件减少
因为无需定义View层接口,因此类文件会有所减少
配合Google其它,写出更优雅的代码
除了上面用到了三个最基础的组件,Google还给我们提供了其它的组件比如databinding,paging,room等,你可以根据自己的需求去选择使用,构建出高品质,更强大的应用程序。
下面是demo,需要的可以下载:
demo
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!