需求背景
传统完成
比如上面的比如,页面由3 个模块数据构成。
我们能够创立一个 ViewModel ,以及 3个 LiveData 来驱动改写对应的 UI 。
class HomeViewModel() : ViewModel() {
private val _newsViewState = MutableLiveData<String>()
val newsViewState: LiveData<String>
get() = _newsViewState
private val _weatherState = MutableLiveData<String>()
val weatherState: LiveData<String>
get() = _weatherState
private val _imageOfTheDayState = MutableLiveData<String>()
val imageOfTheDayState: LiveData<String>
get() = _imageOfTheDayState
fun getNews(){}
fun getWeather(){}
fun getImage(){}
}
这样的完成会有个缺陷,便是跟着事务的迭代,页面的逻辑变得复杂,这儿的 ViewModel 类代码会变复杂,变得臃肿。
这个时分,就可能需求考虑进行拆分 ViewModel。
一种完成办法,便是直接简略地拆分为3个 ViewModel,每个 ViewModel 处理对应的事务。但是这样会带来其他问题,便是在 View 层运用的时分,要判别当时是什么事务,然后再去获取对应的ViewModel,运用起来会比较费事。
优化完成
目标:
- 将 ViewModel 拆分成多个子 ViewModel,每个子 ViewModel 只重视处理本身的事务逻辑。
- 尽量考虑代码的可维护性、可扩展性
Kotlin 托付
- 托付(Delegate)是 Kotlin 的一种语言特性,用于愈加高雅地完成代理模式。
- 本质上便是运用了 by 语法后,编译器会帮忙生成相关代码。
- 类托付: 一个类的办法不在该类中界说,而是直接托付给另一个对象来处理。
- 根底类和被托付类都完成同一个接口,编译时生成的字节码中,继承自 Base 接口的办法都会托付给BaseImpl 处理。
// 根底接口
interface Base {
fun print()
}
// 根底对象
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 被托付类
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 终究调用了 Base#print()
}
具体完成
界说子 ViewModel 的接口,以及对应的完成类
interface NewsViewModel {
companion object {
fun create(): NewsViewModel = NewsViewModelImpl()
}
val newsViewState: LiveData<String>
fun getNews()
}
interface WeatherViewModel {
companion object {
fun create(): WeatherViewModel = WeatherViewModelImpl()
}
val weatherState: LiveData<String>
fun getWeather()
}
interface ImageOfTheDayStateViewModel {
companion object {
fun create(): ImageOfTheDayStateViewModel = ImageOfTheDayStateImpl()
}
val imageState: LiveData<String>
fun getImage()
}
class NewsViewModelImpl : NewsViewModel, ViewModel() {
override val newsViewState = MutableLiveData<String>()
override fun getNews() {
newsViewState.postValue("测验")
}
}
class WeatherViewModelImpl : WeatherViewModel, ViewModel() {
override val weatherState = MutableLiveData<String>()
override fun getWeather() {
weatherState.postValue("测验")
}
}
class ImageOfTheDayStateImpl : ImageOfTheDayStateViewModel, ViewModel() {
override val imageState = MutableLiveData<String>()
override fun getImage() {
imageState.postValue("测验")
}
}
- 把一个大模块,划分成若干个小的事务模块,由对应的 ViewModel 来进行处理,彼此之间尽量保持独立。
- 界说接口类,提供需求对外暴漏的字段和办法
- 界说接口完成类,内部负责完成 ViewModel 的事务细节,修改对应字段值,完成相应办法。
- 这种完成方式,就不需求像上面的比如一样,每次都要多声明一个带划线的私有变量。而且能够对外躲藏更多 ViewModel 的完成细节,封装性更好。
组合 ViewModel
interface HomeViewModel : NewsViewModel, WeatherViewModel, ImageOfTheDayStateViewModel {
companion object {
fun create(activity: FragmentActivity): HomeViewModel {
return ViewModelProviders.of(activity, object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return if (modelClass == HomeViewModelImpl::class.java) {
@Suppress("UNCHECKED_CAST")
val newsViewModel = NewsViewModel.create()
val weatherViewModel = WeatherViewModel.create()
val imageOfTheDayStateImpl = ImageOfTheDayStateViewModel.create()
HomeViewModelImpl(
newsViewModel,
weatherViewModel,
imageOfTheDayStateImpl
) as T
} else {
modelClass.newInstance()
}
}
}).get(HomeViewModelImpl::class.java)
}
}
}
class HomeViewModelImpl(
private val newsViewModel: NewsViewModel,
private val weatherViewModel: WeatherViewModel,
private val imageOfTheDayState: ImageOfTheDayStateViewModel
) : ViewModel(),
HomeViewModel,
NewsViewModel by newsViewModel,
WeatherViewModel by weatherViewModel,
ImageOfTheDayStateViewModel by imageOfTheDayState {
val subViewModels = listOf(newsViewModel, weatherViewModel, imageOfTheDayState)
override fun onCleared() {
subViewModels.filterIsInstance(BaseViewModel::class.java)
.forEach { it.onCleared() }
super.onCleared()
}
}
- 界说接口类 HomeViewModel,继承了多个子 ViewModel 的接口
- 界说完成类 HomeViewModelImpl,组合多个子 ViewModel,并经过 Kotlin 类托付的形式,把对应的接口交给相应的完成类来处理。
- 经过这种方式,能够把对应模块的事务逻辑,拆分到对应的子 ViewModel 中进行处理。
- 假如后续需求新增一个新事务数据,只需新增相应的子模块对应的 ViewModel,而无需修改其他子模块对应的 ViewModel。
- 自界说 ViewModelFactory,提供 create 的静态办法,用于外部获取和创立 HomeViewModel。
运用方式
- 对于 View 层来说,只需求获取 HomeViewModel 就行了。
- 调用露出的办法,最后会托付给对应子 ViewModel 完成类进行处理。
val viewModel = HomeViewModel.create(this)
viewModel.getNews()
viewModel.getWeather()
viewModel.getImage()
viewModel.newsViewState.observe(this) {
}
viewModel.weatherState.observe(this) {
}
viewModel.imageState.observe(this) {
}
扩展
- 上面的比如,HomeViewModel 下面,能够由若干个子 ViewMdeol 构成。
- 跟着事务拓宽,NewsViewModel、WeatherViewModel、ImageOfTheDayStateViewModel,也可能是分别由若干个子 ViewModel 构成。那也能够参照上面的方式,进行完成,最后会构成一棵”ViewModel 树“,各个节点的 ViewModel 负责处理对应的事务逻辑。
总结
这儿只是提供一种拆分 ViewModel 的思路,在项目中进行应用的话,能够根据需求进行改造。
参考文章
slicing-your-viewmodel-with-delegates
Kotlin | 托付机制 & 原理 & 应用 –