ViewModel 是什么

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

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

下图是左侧给出了Activity 经历屏幕旋转而后结束的过程,所处的各种生命周期状态。而右侧是ViewModel的生命周期。

从中可以看出ViewModel的生命周期是长于组件(Activity)的生命周期。

android 如何取出viewmodel 保存的数据 android viewmodel livedata_数据


屏幕旋转后数据得以保存仅是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 执行顺序如下:

android 如何取出viewmodel 保存的数据 android viewmodel livedata_android_02

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

总结

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

而且在使用AndroidStudio创建Fragment的时候提供了创建ViewModel选项,极大的方便了开发。

android 如何取出viewmodel 保存的数据 android viewmodel livedata_android_03

参考