zoukankan      html  css  js  c++  java
  • Android Jetpack 库架构组件 ViewModel+LiveData 基础使用

    ViewModel 是什么

    ViewModel 类旨在以注重生命周期的方式 存储和管理 界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

    为什么ViewModel 类中的数据可在发生屏幕旋转等配置更改后继续留存?
    因为ViewModel的生命周期长于组件(Activity/Fragment)的生命周期

    下图是左侧给出了Activity 经历屏幕旋转而后结束的过程,所处的各种生命周期状态。而右侧是ViewModel的生命周期。
    从中可以看出ViewModel的生命周期是长于组件(Activity)的生命周期。
    在这里插入图片描述
    屏幕旋转后数据得以保存仅是ViewModel的其中一个优势,生命周期比组件长的优势还可以:

    • 减少资源浪费:避免因配置更改而重新创建对象
    • 避免内存泄露:界面控制器经常需要做异步调用,而异步调用需要在一段时间后才返回结果。如果界面关闭之后数据在还没返回,且其中的一些引用还存在,那么内存泄露就不可避免了。通常我们会在页面关闭的时候手动清除引用。在生命周期中我们可以看到ViewModel还有一个方法onCleared(),我们可在该方法手动清除引用。
      在这里插入图片描述
      谷歌在生命感知组件的最佳做法中,推荐我们使用ViewModel+LiveData的组合。
      在这里插入图片描述
      既然如此,那我们先来了解何为LiveData

    LiveData 是什么

    LiveData 是一种 可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,所以它能在不同生命周期处理不同的操作。

    由于LiveData是可观察的数据存储类且有生命周期感知能力,因此它有具备如下优势:

    • 确保界面符合数据状态:LiveData 遵循观察者模式。当生命周期改变数据也会刷新

    • 不会发生内存泄露:观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。

    • 不会因 Activity 停止而导致崩溃:如果观察者的生命周期处于非活跃状态,则它不会接收任何 LiveData 事件。

    • 不再需要手动处理生命周期:LiveData 将自动管理所有这些操作

    • 数据始终保持最新状态:如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。

    • 适当的配置更改:如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。

    • 共享数据:数据存储在ViewModel中,需要相应资源的任何(Activity/Fragment)等观察者只需观察 LiveData 对象

    ViewModel+LiveData 使用步骤

    示例:
    (1)实现倒计时功能
    (2)ViewModel传参获取数据库的数据展示

    1. Module -> build.gradle的引入

    版本依赖查看:https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#declaring_dependencies

    	def lifecycle_version = "2.2.0"
    
    	// ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    	// 还有一些可选项,可查看上面的版本依赖链接
    
    1. 创建Viewmodel(推荐创建BaseViewModel,之后再创建具体的ViewModel继承BaseViewModel)
    /**
     * ViewModel 基类
     */
    open class BaseViewModel : ViewModel() {
        override fun onCleared() {
            super.onCleared()
            Log.e("CommonViewModel","onCleared")
        }
    }
    
    (示例1:倒计时功能)
    
    class MineViewModel : BaseViewModel() {
        private val periodTime = 1000L
        private val mElapsedRealTime = MutableLiveData<Long>()
        private var mInitialTime: Long = 0
    
        // Create a LiveData with a Long
        val countDownTime: MutableLiveData<Long> by lazy {
            mElapsedRealTime
        }
        /**
         * 开启倒计时
         */
        fun getTime(){
            mInitialTime = SystemClock.elapsedRealtime()
            val timer = Timer()
            val timeTask = object : TimerTask() {
                override fun run() {
                    val newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000
                    if (newValue < 10){
                        mElapsedRealTime.postValue(newValue)
                    }else{
                        timer.cancel()
                    }
                }
            }
            timer.scheduleAtFixedRate(timeTask,periodTime,periodTime)
        }
    }
    
    
    (示例2:ViewModel传参获取数据库的数据展示)
    
    class HomeViewModel(context: Context): BaseViewModel() {
        companion object {
            private const val PAGE_SIZE = 15
            private const val ENABLE_PLACEHOLDERS = false
        }
        val mContext = context
        val dao = StudentDb.get(mContext).studentDao()
    
        val allStudents = LivePagedListBuilder(dao.getAllStudent(), PagedList.Config.Builder()
            .setPageSize(PAGE_SIZE)                         //配置分页加载的数量
            .setEnablePlaceholders(ENABLE_PLACEHOLDERS)     //配置是否启动PlaceHolders
            .setInitialLoadSizeHint(PAGE_SIZE)              //初始化加载的数量
            .build()).build()
    }
    
    1. 创建 Activity(推荐Fragment)中使用
    (示例1:倒计时功能)
    class MineFragment : BaseFragment(),MineContract.View {
    
        private lateinit var viewModel: MineViewModel
    
        override fun initData() {
        	// 创建实例
            viewModel = ViewModelProviders.of(this).get(MineViewModel::class.java)
            // 观察数据
            viewModel.countDownTime.observe(this, Observer<Long> { aLong ->
                //Update UI
                tv_name.text = "time = " + aLong!!
            })
        }
        override fun initView() {
            // 订阅事件
            lifecycle.addObserver(minePresenter)
            // 获取数据
            btn_get_data.setOnClickListener {
                viewModel.getTime()
            }
        }
          // 省略部分代码,具体可看下方源码链接
    }
        
    (示例2:ViewModel传参获取数据库的数据展示)
    class HomeJetpackFragment : BaseFragment() {
        private val TAG = HomeJetpackFragment::class.java.simpleName
    
        private val viewModel by lazy(LazyThreadSafetyMode.NONE) {
            ViewModelProviders.of(this, object : ViewModelProvider.Factory {
                // 传递 context
                override fun <T : ViewModel?> create(modelClass: Class<T>): T = HomeViewModel(
                    BaseApplication.context as Application
                ) as T
            }).get(HomeViewModel::class.java)
        }
    
        override fun initView() {
            val adapter = StudentAdapter()
            val layoutManager = LinearLayoutManager(activity)
            rv_list.layoutManager = layoutManager
            rv_list.adapter = adapter
            // 将数据的变化反映到UI上
            viewModel.allStudents.observe(this, Observer {
                adapter.submitList(it)
            })
        }
        // 省略部分代码,具体可看下方源码链接
    }
    

    Tip:
    initDatainitView 执行顺序如下:
    在这里插入图片描述

    详细使用代码请参见:YGragon/FrameDemo

    总结

    使用ViewModel+LiveData的模式,可以很好的在组件的不同生命周期处理数据。这是由于LiveData是生命周期感知型类所带来的特性。ViewModel中专注于处理数据,和UI组件彻底解耦。

    而且在使用AndroidStudio创建Fragment的时候提供了创建ViewModel选项,极大的方便了开发。
    在这里插入图片描述

    参考

    上车

    佛系原创号主
    在这里插入图片描述

  • 相关阅读:
    好用的PasswordTextBox.
    可以修改Autocomplete高度和宽度的TextBox.(ComboBox也试用)
    Show WER and DMP file information
    在webBrowser中触发html页面中的javaScript.
    Trigger in sql server
    黑客来了。。。键盘钩子,听起来很高端。
    Send email
    (VB.net)自定义TableLayoutPanel使它能够在运行时用鼠标改变行高和列宽。
    (C#) Format the cell of DataGridView based on the TextBox.Text
    可以用来测显示屏的inch数。
  • 原文地址:https://www.cnblogs.com/gdragon/p/13210569.html
Copyright © 2011-2022 走看看