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的时候,关键是划分功能属于哪一个层次,然后,再确定引用关系。划分功能属于哪个层次,可以依据单一职责原则,让功能代码原子化,再在这一基础上去区分层次。
 
 

android mvvm实例解析的更多相关文章

  1. 【转】Android HAL实例解析

    原文网址:http://www.embedu.org/Column/Column339.htm 作者:刘老师,华清远见嵌入式学院讲师. 一.概述 本文希望通过分析台湾的Jollen的mokoid 工程 ...

  2. Android HAL实例解析

    一.概述 本文希望通过分析台湾的Jollen的mokoid 工程代码,和在s5pc100平台上实现过程种遇到的问题,解析Andorid HAL的开发方法. 二.HAL介绍 现有HAL架构由Patric ...

  3. Android AIDL实例解析

    AIDL这项技术在我们的开发中一般来说并不是很常用,虽然自己也使用新浪微博的SSO登录,其原理就是使用AIDL,但是自己一直没有动手完整的写过AIDL的例子,所以就有了这篇简单的文章. AIDL(An ...

  4. Android实例-Delphi开发蓝牙官方实例解析(XE10+小米2+小米5)

    相关资料:1.http://blog.csdn.net/laorenshen/article/details/411498032.http://www.cnblogs.com/findumars/p/ ...

  5. Android开发之IPC进程间通信-AIDL介绍及实例解析

    一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用 ...

  6. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  7. [转] Android Volley完全解析(一),初识Volley的基本用法

    版权声明:本文出自郭霖的博客,转载必须注明出处.   目录(?)[-] Volley简介 下载Volley StringRequest的用法 JsonRequest的用法   转载请注明出处:http ...

  8. Android IntentService完全解析 当Service遇到Handler

    一 概述 大家都清楚,在Android的开发中,凡是遇到耗时的操作尽可能的会交给Service去做,比如我们上传多张图,上传的过程用户可能将应用置于后台,然后干别的去了,我们的Activity就很可能 ...

  9. Android Volley完全解析

    1. Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行H ...

  10. Android Bitmap 全面解析(四)图片处理效果对比 ...

    对比对象: UIL Volley 官方教程中的方法(此系列教程一里介绍的,ImageLoader的处理方法和官方的差不多) -------------------------------------- ...

随机推荐

  1. spring,springBoot配置类型转化器Converter以及FastJsonHttpMessageConverter,StringHttpMessageConverter 使用

    转载请注明出处: https://i.cnblogs.com/posts/edit;postId=14045507 spring,spring boot 等框架项目通过@RequestBody,@Re ...

  2. Git-签名-user-email

  3. [转帖]Oracle数据库中ITL详解

    首先说明这篇文章是转载的,原文地址:http://blog.sina.com.cn/s/blog_616b428f0100lwvq.html 1.什么是ITL ITL(Interested Trans ...

  4. [转帖]ORACLE 并行(PARALLEL)实现方式及优先级

      http://blog.itpub.net/25542870/viewspace-2120924/ 一.      Parallel query 默认情况下session 是ENABLE状态 1. ...

  5. [转帖]《Linux性能优化实战》笔记(21)—— 网络性能优化思路

    一. 确定优化目标 优化前,我会先问问自己,网络性能优化的目标是什么?实际上,虽然网络性能优化的整体目标,是降低网络延迟(如 RTT)和提高吞吐量(如BPS 和 PPS),但具体到不同应用中,每个指标 ...

  6. [转帖]全球CPU市场格局(2022)

    https://www.eet-china.com/mp/a222817.html 本文选自"2022年国产服务器CPU研究框架",重点分析2022年CPU产业链.CPU市场规模. ...

  7. [转帖]性能分析之TCP全连接队列占满问题分析及优化过程(转载)

    https://www.cnblogs.com/wx170119/p/12068005.html 前言 在对一个挡板系统进行测试时,遇到一个由于TCP全连接队列被占满而影响系统性能的问题,这里记录下如 ...

  8. [转帖]Dapper,大规模分布式系统的跟踪系统

    http://bigbully.github.io/Dapper-translation/ 作者:Benjamin H. Sigelman, Luiz Andr´e Barroso, Mike Bur ...

  9. [转帖]Elasticsearch 技术分析(五):如何通过SQL查询Elasticsearch

    https://www.cnblogs.com/jajian/p/10053504.html 前言# 这篇博文本来是想放在全系列的大概第五.六篇的时候再讲的,毕竟查询是在索引创建.索引文档数据生成和一 ...

  10. [转帖]HTTP与HTTPS超文本传输协议的区别是什么

    https://www.likecs.com/show-308649882.html 随着越来越多的网站使用HTTPS加密,现在HTTPS的使用已经成了硬性要求了.虽然说https是http的安全版, ...