MVVM 架构是继mvc架构后衍生出的一个新的架构, 最早于 2005 年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出,并且应用在微软的软件开发中,Android开发中常见的模式,有 MVC,MVP,MVVM。Google15年推出databinding 和 2018年推出 LiveData、ViewModel 库之后,把MVVM正式的应用在 android 上 推进了高潮。本文最后会写一个官方所支持的demo
备注:摘自任玉刚公众号的一篇文章
题外话:1.项目中不一定用LiveData,Rxjava本身就是观察者模式,如果项目中引用到Rxjava,可以抛弃LiveData,但是请确保正确处理应用的生命周期。特别是,确保在相关的 LifecycleOwner 停止时暂停数据流,并在相关的 LifecycleOwner 销毁时销毁这些数据流。官方说法
题外话:2.MVVM可以不用DataBinding,但是android中Google既然已经支持,还是用DataBinding来处理View的更改,在android中 DataBinding 是实现MVVM的主要组件
在应用模块的 build.gradle 文件中添加 dataBinding 元素
android {
...
dataBinding {
enabled = true
}
}
之后你在xml的根布局中 ,mac按住command+return ,window 按住 art+enter 就会出现这样的标记,选第一个就会出现DataBinding的layout。如果没有这个选项,重启studio就可以了
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
使用方式
val user: LiveData = liveData {
val data = getNetData() // loadUser is a suspend function.
emit(data) //发射给出去
}
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
会用方式
class MainViewModel(private val repository: MainUserRepository) : ViewModel() {
var userData = MutableLiveData()
var progressShow = MutableLiveData()
fun click(view: View) {
getUser()
}
private fun getUser() {
//在这里开协程
viewModelScope.launch {
progressShow.value = true
userData.value = repository.getUser()
progressShow.value = false
}
}
}
implementation "androidx.activity:activity-ktx:1.1.0"
使用方式:直接在acitiviy中调用下面的方法,另外这个还需要在app的build.gradle中加入一个配置。当然了,我们也可以用别的懒加载方式加载ViewModel,就可以不用引用这个依赖了,比如
//不用添加此依赖的方式获取懒加载ViewModel
val viewModel:MyViewModel by lazy { ViewModelProvider(this).get(MyViewModel::class.java) }
//使用此依赖需要配置
android {
kotlinOptions {
jvmTarget = "1.8"
}
...
}
// 这里只能用val来修饰
private val viewModel: MyViewModel by viewModels ()
implementation "androidx.fragment:fragment-ktx:1.2.4"
使用方式
private val viewModel : MyViewModel by viewModels()
数据类
data class User(var name:String,var age:Int)
UesrActivity
class UserActivity : AppCompatActivity() {
//创建ViewModel方式1
// private val viewModel: UserViewModel by viewModels()
//创建ViewModel方式2
// private val viewModel:UserViewModel by lazy { ViewModelProvider(this).get(UserViewModel::class.java) }
private val viewModel: UserViewModel by viewModels {
// 通过InjectorUtils类拿到对应的ViewModelFactory,这种方法可以定义构造方法带参的ViewModel
InjectorUtils.provideUserViewModelFactory()
}
private var progressDialog: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding =
DataBindingUtil.setContentView<ActivityUserBinding>(this, R.layout.activity_user)
//绑定生命周期,声明这个 Activity 为 Data Binding 的 lifecycleOwner
binding.lifecycleOwner = this
//设置xml中的viewModel
binding.viewmodel = viewModel
// viewModel的progressShow 来判断是否是加载中的状态
viewModel.progressShow.observe(this, Observer<Boolean> {
if (it) showLoading()
else stopLoading()
})
}
/**
* 显示加载框
*/
private fun showLoading() {
if (progressDialog == null) {
progressDialog = ProgressDialog(this)
progressDialog!!.setMessage("加载中")
}
progressDialog!!.show()
}
/**
* 关闭加载框
*/
private fun stopLoading() {
progressDialog?.dismiss()
}
companion object {
fun startMe(activity: Activity) {
activity.startActivity(Intent(activity, UserActivity::class.java))
}
}
}
activity_user.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewmodel"
type="com.nzy.mvvmsimple.user.UserViewModel" />
<import type="android.view.View" />
data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewmodel.userData.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/bv_get_user"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:onClick="@{viewmodel::click}"
android:text="获取网络数据"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_name" />
<ProgressBar
android:layout_width="50dp"
android:layout_height="50dp"
android:visibility="@{viewmodel.progressShow ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
androidx.constraintlayout.widget.ConstraintLayout>
layout>
InjectorUtils类:用来获取各种各样的ViewModelFatory
object InjectorUtils {
fun provideUserViewModelFactory(): UserViewModelFactory {
val repository = getUserRepository()
return UserViewModelFactory(repository)
}
private fun getUserRepository(): UserRepository {
return UserRepository.getInstance()
}
}
UserViewModelFactory
class UserViewModelFactory(
private val repository: UserRepository
) : ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
// 传入一个 Repository 参数
return UserViewModel(repository) as T
}
}
UserRepository:获取数据的网络或者数据库
/**
* 获取用户信息的Repository
*/
class UserRepository private constructor() {
/**
* 获取User的接口
*/
suspend fun getUser(): User? {
delay(3000)
return User("王者荣耀之马大黑", 18)
}
companion object {
// For Singleton instantiation
@Volatile
private var instance: UserRepository? = null
fun getInstance() =
instance ?: synchronized(this) {
instance ?: UserRepository().also { instance = it }
}
}
}