前言:对比MVP架构,ViewModel+LiveData实现的MVVM架构代码更加简洁,同时由于ViewModel和Retrofit、Room均可使用Coroutine,简化了获取数据的订阅操作。(ViewModel替代P,LiveData替代V)
实现:
1、ViewModel基类封装
open class BaseViewModel : ViewModel() {
val isShowLoadingLiveData = MutableLiveData<Boolean>()
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
2、Activity基类
通过抽象createViewModel()方法将创建ViewModel对象交给业务层,方便构造方法传参和activity与fragment共用同一个ViewModel,实现数据共享。
abstract class BaseMvvmActivity<T : BaseViewModel> : BaseActivity() {
lateinit var mViewModel: T
private val mLoadingDialog: ProgressDialog by lazy { ProgressDialog(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewModel = creatViewModel()
ViewModelProvider(this, ViewModelFactory(mViewModel)).get(mViewModel::class.java)//ViewMode添加生命周期能力
mViewModel.isShowLoadingLiveData.observe(this, Observer {isShowLoading ->
if (isShowLoading){
mLoadingDialog.show()
}else{
mLoadingDialog.dismiss()
}
})
}
/*
创建ViewMode
*/
protected abstract fun creatViewModel(): T
}
3、ViewModel工厂类
class ViewModelFactory constructor(private val viewModel: ViewModel): ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return viewModel as T
}
}
4、业务层具体ViewModel
fun login(mobile: String, authCode: String){
isShowLoadingLiveData.value = true
viewModelScope.launch {
kotlin.runCatching {
mUserReapository.loginMvvm(LoginReq(mobile, authCode))
}.onSuccess {
isShowLoadingLiveData.value = false
ARouter.getInstance().build(RouterPath.HualalaApp.PATH_HOME).navigation()
}.onFailure {
isShowLoadingLiveData.value = false
}
}
}
注:viewModelScope
默认使用 Dispatchers.Main,由于是主线程方便更新UI(如onSuccess和onFailure均在主线程执行)
5、数据层(suspend使用)
interface UserApi {
@POST("login/rider")
suspend fun loginMvvm(@Body req: LoginReq): BaseRespone<LoginRes>
}
class UserReapository @Inject constructor() {
private val mUserApi = RetrofitFactory.instance.create(BaseConstant.SERVICE_HOST).create(UserApi::class.java)
suspend fun loginMvvm(loginReq: LoginReq): LoginRes{
return mUserApi.loginMvvm(loginReq).convertMvvm()
}
}
fun <T> BaseRespone<T>.convertMvvm(): T{
when(this.code){
ResultCode.SUCCESS -> return this.data
ResultCode.ACCESS_TOKEN_EXPIRE -> {
AppPrefsUtils.remove(AppPrefsUtils.ACCESS_TOKEN)
AppManager.instance.finishAllActivity()
ARouter.getInstance().build(RouterPath.HualalaUser.PATH_LOGIN).navigation()
throw Throwable(this.msg)
}
else -> throw Throwable(this.msg)
}
}
6、登录页面
class LoginActivity : BaseMvvmActivity<LoginViewModel>(){
override fun creatViewModel(): LoginViewModel {
return LoginViewModel()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
toHomeTv.setOnClickListener {
mViewModel.login("18600881234", "123456")
}
}
}
7、ViewModel生命周期实现原理
FragmentActivity和Fragment中定义了存放ViewModel集合的ViewModelStore,通过ViewModelProvider将创建的ViewModel添加至ViewModelStore中,然后在FragmentActivity和Fragment生命周期中管理ViewModelStore从而实现生命周期
8、LiveData在fragment中注意点
最好不要直接将fragment作为LifecycleOwner使用。某些场景会导致重复订阅(如在onViewCreated()中进行订阅,而LiveData解绑是在fragment的onDestory中进行,在fragment复用场景,如viewpager中,复用fragment不会onDestory而再执行onViewCreated导致重复订阅,解绑和绑定生命周期不匹配导致的),所以我们可以使用fragment中的getViewLifecycleOwner()获取的LifecycleOwner对象进行订阅,该对象是在onDestroyView()方法中进行解绑,