赞
踩
这张图清晰地展示了MVVM的三个模块:Activity / Fragment
为View
层,ViewModel + LiveData
为ViewModel
层,Repository
(管理本地和远端数据)为Model
层。
理清了MVVM的层次,接下来尝试对其进行简单的封装。
1.BaseRepository
提取了生成Service
类的方法apiService
供子类使用。
提取了请求接口方法request
,出现异常情况则throw
,让ViewModel
层处理。
abstract class BaseRepository { protected fun <T> apiService(tClass: Class<T>): T { return ApiClient.createService(tClass) } @Throws(Exception::class) protected suspend fun <T> request(block: suspend () -> BaseData<T>): T { val baseData = block() if (baseData.errorCode == 0) { if (baseData.data == null) { throw Exception("baseData.data is null!") } return baseData.data } else { throw ApiException(baseData.errorCode, baseData.errorMsg) } } }
2.BaseViewModel
提取了launch
方法,可以一次调用多个接口;处理Model
层抛出的异常。
abstract class BaseViewModel : ViewModel() { private val _dataLoading = MutableLiveData<Boolean>() val dataLoading: LiveData<Boolean> = _dataLoading protected fun launch( block: suspend () -> Unit, error: suspend (Int) -> Unit = { } ) { viewModelScope.launch { _dataLoading.value = true try { block.invoke() } catch (e: Exception) { when (e) { is ApiException -> { // TODO: use Toast to show errorMsg error(e.code) } is ConnectException, is UnknownHostException, is SocketTimeoutException -> { // TODO: use Toast to show exception message Log.e("Exception", e.localizedMessage) } } } finally { _dataLoading.value = false } } } }
3.BaseVmActivity
提取viewModel
字段及创建方法;添加viewModel
的dataLoading
字段的监听,实现统一的Loading管理。
abstract class BaseVmActivity<VDB : ViewDataBinding, VM : BaseViewModel> : BaseActivity<VDB>() { protected val viewModel by lazy { ViewModelProvider(this).get(getVmClass()) } private val loadingDialog by lazy { ContentLoadingDialog(this) } abstract fun getVmClass(): Class<VM> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addObserver() } open fun addObserver() { viewModel.dataLoading.observe(this) { if (it) { loadingDialog.showDialog() } else { loadingDialog.hideDialog() } } } }
1.HomeRepository
添加hotkeys
和banners
方法,请求 玩Android 开放API 的搜索热词及首页banner接口。
class HomeRepository : BaseRepository() {
suspend fun hotkeys(): List<HotkeyModel> {
return request {
apiService(HomeService::class.java).hotkeys()
}
}
suspend fun banners(): List<Any> {
return request {
apiService(HomeService::class.java).banners()
}
}
}
2.HomeViewModel
调用HomeRepository
的hotkeys
和banners
方法(未处理banners
接口返回数据,这里只做多个接口调用的演示),返回搜索热词列表,通过DataBinding
自动将处理后的hotkeyText
显示到TextView
上。
class HomeViewModel : BaseViewModel() { private val repository by lazy { HomeRepository() } private val hotkeyList = MutableLiveData<List<HotkeyModel>>() private var _hotkeyText: LiveData<String> = hotkeyList.distinctUntilChanged().map { createHotkeyText(it) } val hotkeyText: LiveData<String> = _hotkeyText fun hotkeys() { launch({ hotkeyList.value = repository.hotkeys() repository.banners() }) } private fun createHotkeyText(list: List<HotkeyModel>): String { val text = StringBuilder() for (key in list) { text.append("${key.name}\n") } return text.toString() } }
3.MvvmActivity
利用DataBinding
将viewModel
赋值给viewmodel
,实现数据绑定。
class MvvmActivity : BaseVmActivity<ActivityMvvmBinding, HomeViewModel>() {
override fun getBinding(): ActivityMvvmBinding {
return ActivityMvvmBinding.inflate(layoutInflater).apply {
viewmodel = viewModel
lifecycleOwner = this@MvvmActivity
}
}
override fun getVmClass(): Class<HomeViewModel> {
return HomeViewModel::class.java
}
}
接口请求太快了,loading都没有来得及显示。。。
上面只展示了MVVM
在Activity中
的使用,demo里也有在Fragment
中使用的代码。如需参考完整代码,请移步Github仓库:MVVM Demo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。