MVVM架构,将整个应用分为三层,View层,VM层,Model层。其中View层单向引用VM层,VM层单向引用Model层。如上图。单向引用,而非双向引用,这是MVVM与MVP最大的区别。View层,只是单向引用VM层,VM层不需要引用View层,但是却可以更新View层。这是通过VM层的观察者模式实现的,在这里使用架构组件LiveData,观察者注册LiveData,当LiveData数据发生变更的时候,就会通知注册的观察者。VM层,执行业务逻辑,获取Model层的数据,Model层的数据由repository来提供。举例子:ChooseAreaFragment是View层,它持有ViewModel,它可以监听相关数据,相关数据发生变化的时候,对应的UI就会被更新。比如:dataChanged数据发生变化,就会执行定义的观察者操作。viewModel.dataChanged.observe(this, Observer {adapter.notifyDataSetChanged()listView.setSelection(0)closeProgressDialog()})

class ChooseAreaFragment : Fragment() {private val viewModel by lazy { ViewModelProviders.of(this, InjectorUtil.getChooseAreaModelFactory()).get(ChooseAreaViewModel::class.java) }private var progressDialog: ProgressDialog? = nullprivate lateinit var adapter: ArrayAdapteroverride fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {val view = inflater.inflate(R.layout.choose_area, container, false)val binding = DataBindingUtil.bind(view)binding?.viewModel = viewModelreturn view}override fun onActivityCreated(savedInstanceState: Bundle?) {super.onActivityCreated(savedInstanceState)adapter = ChooseAreaAdapter(context!!, R.layout.simple_item, viewModel.dataList)listView.adapter = adapterobserve()}private fun observe() {viewModel.currentLevel.observe(this, Observer { level ->when (level) {LEVEL_PROVINCE -> {titleText.text = "中国"backButton.visibility = View.GONE}LEVEL_CITY -> {titleText.text = viewModel.selectedProvince?.provinceNamebackButton.visibility = View.VISIBLE}LEVEL_COUNTY -> {titleText.text = viewModel.selectedCity?.cityNamebackButton.visibility = View.VISIBLE}}})viewModel.dataChanged.observe(this, Observer {adapter.notifyDataSetChanged()listView.setSelection(0)closeProgressDialog()})viewModel.isLoading.observe(this, Observer { isLoading ->if (isLoading) showProgressDialog()else closeProgressDialog()})viewModel.areaSelected.observe(this, Observer { selected ->if (selected && viewModel.selectedCounty != null) {if (activity is MainActivity) {val intent = Intent(activity, WeatherActivity::class.java)intent.putExtra("weather_id", viewModel.selectedCounty!!.weatherId)startActivity(intent)activity?.finish()} else if (activity is WeatherActivity) {val weatherActivity = activity as WeatherActivityweatherActivity.drawerLayout.closeDrawers()weatherActivity.viewModel.weatherId = viewModel.selectedCounty!!.weatherIdweatherActivity.viewModel.refreshWeather()}viewModel.areaSelected.value = false}})if (viewModel.dataList.isEmpty()) {viewModel.getProvinces()}}/*** 显示进度对话框*/private fun showProgressDialog() {if (progressDialog == null) {progressDialog = ProgressDialog(activity)progressDialog?.setMessage("正在加载...")progressDialog?.setCanceledOnTouchOutside(false)}progressDialog?.show()}/*** 关闭进度对话框*/private fun closeProgressDialog() {progressDialog?.dismiss()}companion object {const val LEVEL_PROVINCE = 0const val LEVEL_CITY = 1const val LEVEL_COUNTY = 2}}

VM层,ViewModel:使用LiveData包装被View层监听的数据,在VM层数据发生的变化,会通知到View层,但却无需要View层的引用。因为LiveData应用了观察者模式,注册的观察者,在数据发生变化的时候,会自动通知观察者。如下,currentLevel,dataChanged,isLoading等都是使用LiveData包装的,意味着,它们发生变化的时候View层会监听得到,从而进行相应的更新操作。在VM层,持有Model层的引用,Model层的数据获取,网络请求,都依赖repository实现。

class ChooseAreaViewModel(private val repository: PlaceRepository) : ViewModel() {var currentLevel = MutableLiveData()var dataChanged = MutableLiveData()var isLoading = MutableLiveData()var areaSelected = MutableLiveData()var selectedProvince: Province? = nullvar selectedCity: City? = nullvar selectedCounty: County? = nulllateinit var provinces: MutableListlateinit var cities: MutableListlateinit var counties: MutableListval dataList = ArrayList()fun getProvinces() {currentLevel.value = LEVEL_PROVINCElaunch {provinces = repository.getProvinceList()dataList.addAll(provinces.map { it.provinceName })}}private fun getCities() = selectedProvince?.let {currentLevel.value = LEVEL_CITYlaunch {cities = repository.getCityList(it.provinceCode)dataList.addAll(cities.map { it.cityName })}}private fun getCounties() = selectedCity?.let {currentLevel.value = LEVEL_COUNTYlaunch {counties = repository.getCountyList(it.provinceId, it.cityCode)dataList.addAll(counties.map { it.countyName })}}fun onListViewItemClick(parent: AdapterView, view: View, position: Int, id: Long) {when {currentLevel.value == LEVEL_PROVINCE -> {selectedProvince = provinces[position]getCities()}currentLevel.value == LEVEL_CITY -> {selectedCity = cities[position]getCounties()}currentLevel.value == LEVEL_COUNTY -> {selectedCounty = counties[position]areaSelected.value = true}}}fun onBack() {if (currentLevel.value == LEVEL_COUNTY) {getCities()} else if (currentLevel.value == LEVEL_CITY) {getProvinces()}}private fun launch(block: suspend () -> Unit) = viewModelScope.launch {try {isLoading.value = truedataList.clear()block()dataChanged.value = dataChanged.value?.plus(1)isLoading.value = false} catch (t: Throwable) {t.printStackTrace()Toast.makeText(CoolWeatherApplication.context, t.message, Toast.LENGTH_SHORT).show()dataChanged.value = dataChanged.value?.plus(1)isLoading.value = false}}}

Model层:在这个例子中,Model层对外提供的方法是getProvinceList,getCityList,getCountyList。它的数据来源,可能是数据库Dao,或者是网络,各自的实现,再委托到具体的方法去实现。

class PlaceRepository private constructor(private val placeDao: PlaceDao, private val network: CoolWeatherNetwork) {suspend fun getProvinceList() = withContext(Dispatchers.IO) {var list = placeDao.getProvinceList()if (list.isEmpty()) {list = network.fetchProvinceList()placeDao.saveProvinceList(list)}list}suspend fun getCityList(provinceId: Int) = withContext(Dispatchers.IO) {var list = placeDao.getCityList(provinceId)if (list.isEmpty()) {list = network.fetchCityList(provinceId)list.forEach { it.provinceId = provinceId }placeDao.saveCityList(list)}list}suspend fun getCountyList(provinceId: Int, cityId: Int) = withContext(Dispatchers.IO) {var list = placeDao.getCountyList(cityId)if (list.isEmpty()) {list = network.fetchCountyList(provinceId, cityId)list.forEach { it.cityId = cityId }placeDao.saveCountyList(list)}list}companion object {private var instance: PlaceRepository? = nullfun getInstance(placeDao: PlaceDao, network: CoolWeatherNetwork): PlaceRepository {if (instance == null) {synchronized(PlaceRepository::class.java) {if (instance == null) {instance = PlaceRepository(placeDao, network)}}}return instance!!}}}

以上就是MVVM的实例解析。应用MVVM的时候,关键是划分功能属于哪一个层次,然后,再确定引用关系。划分功能属于哪个层次,可以依据单一职责原则,让功能代码原子化,再在这一基础上去区分层次。 版权声明:作者:ttylinux    出处:http://www.cnblogs.com/ttylinux/    本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。