android mvvm实例解析
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? = null private lateinit var adapter: ArrayAdapter<String> override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.choose_area, container, false) val binding = DataBindingUtil.bind<ChooseAreaBindingImpl>(view) binding?.viewModel = viewModel return view } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) adapter = ChooseAreaAdapter(context!!, R.layout.simple_item, viewModel.dataList) listView.adapter = adapter observe() } 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?.provinceName backButton.visibility = View.VISIBLE } LEVEL_COUNTY -> { titleText.text = viewModel.selectedCity?.cityName backButton.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 WeatherActivity weatherActivity.drawerLayout.closeDrawers() weatherActivity.viewModel.weatherId = viewModel.selectedCounty!!.weatherId weatherActivity.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 = 0 const val LEVEL_CITY = 1 const 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<Int>() var dataChanged = MutableLiveData<Int>() var isLoading = MutableLiveData<Boolean>() var areaSelected = MutableLiveData<Boolean>() var selectedProvince: Province? = null var selectedCity: City? = null var selectedCounty: County? = null lateinit var provinces: MutableList<Province> lateinit var cities: MutableList<City> lateinit var counties: MutableList<County> val dataList = ArrayList<String>() fun getProvinces() { currentLevel.value = LEVEL_PROVINCE launch { provinces = repository.getProvinceList() dataList.addAll(provinces.map { it.provinceName }) } } private fun getCities() = selectedProvince?.let { currentLevel.value = LEVEL_CITY launch { cities = repository.getCityList(it.provinceCode) dataList.addAll(cities.map { it.cityName }) } } private fun getCounties() = selectedCity?.let { currentLevel.value = LEVEL_COUNTY launch { 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 = true dataList.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? = null fun getInstance(placeDao: PlaceDao, network: CoolWeatherNetwork): PlaceRepository { if (instance == null) { synchronized(PlaceRepository::class.java) { if (instance == null) { instance = PlaceRepository(placeDao, network) } } } return instance!! } } }
以上就是MVVM的实例解析。应用MVVM的时候,关键是划分功能属于哪一个层次,然后,再确定引用关系。划分功能属于哪个层次,可以依据单一职责原则,让功能代码原子化,再在这一基础上去区分层次。