文章目录

  • Jetpack ViewModel
  • 概述
  • 添加依赖库
  • ViewModel的生命周期
  • 基本使用
  • AndroidViewModel
  • Fragment之间共享数据
  • ViewModel与onSaveInstanceState()区别
  • 代码下载


Jetpack ViewModel

概述

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

添加依赖库

//ktx
implementation "androidx.activity:activity-ktx:1.2.3"
implementation "androidx.fragment:fragment-ktx:1.3.6"

//Lifecycle
def lifecycle_version = "2.5.0"
//LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
//ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

ViewModel的生命周期

ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 LifecycleViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。

由于ViewModel的生命周期长于Activity,因此不能将View或者Activity的context传给ViewModel,否则会引起内存泄露。

android View的生命周期 android viewmodel生命周期_viewModel

基本使用

android View的生命周期 android viewmodel生命周期_数据_02

android View的生命周期 android viewmodel生命周期_viewModel_03

ViewModel类实现

class MyViewModel : ViewModel() {
    private val _countLiveData = MutableLiveData<Int>()
    val countLiveData = _countLiveData

    private var count = 0

    fun increase() {
        _countLiveData.postValue(++count)
    }

    fun decrease() {
        _countLiveData.postValue(--count)
    }
}

获取ViewModel对象,方式一

var viewModel: MyViewModel = ViewModelProvider(this).get(MyViewModel::class.java)

获取ViewModel对象,方式二

val viewModel: MyViewModel by viewModels()

在Activity中使用

class ViewModelSimpleActivity : BaseActivity() {
    private lateinit var tvCount: TextView
    private lateinit var tvScreen: TextView

    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model_simple)
        Log.e(VIEWMODEL, "onCreate")

        initView()
        val screenOrientation =
        if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "竖屏" else "横屏"
        tvScreen.text = "屏幕方向:$screenOrientation"

        viewModel.countLiveData.observe(this, object : Observer<Int> {
            override fun onChanged(t: Int?) {
                Log.e(VIEWMODEL, "onChanged")
                tvCount.text = t.toString()
            }
        })
    }

    private fun initView() {
        tvCount = findViewById(R.id.tv_count)
        tvScreen = findViewById(R.id.tv_screen)
    }

    fun clickIncrease(v: View) {
        viewModel.increase()
    }

    fun clickDecrease(v: View) {
        viewModel.decrease()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(VIEWMODEL, "onDestroy")
    }
}

AndroidViewModel

因为ViewModel对象的存活时间较长,因此不能将Activity的Context传递给ViewModel,否则会引起内存泄露。如果需要使用context,这时可以使用AndroidViewModel接收Application的对象。

class MyAndroidViewModel(application: Application) : AndroidViewModel(application) {
    private val _countLiveData = MutableLiveData<Int>()
    val countLiveData = _countLiveData

    private var count = 0

    fun increase() {
        _countLiveData.postValue(++count)
    }

    fun decrease() {
        _countLiveData.postValue(--count)
    }
}

Fragment之间共享数据

优点:

  • Activity不需要做其他操作
  • Fragment之间没有任何干扰,只需要约定ViewModel

android View的生命周期 android viewmodel生命周期_数据_04

SharedViewModel类

class SharedViewModel : ViewModel() {
    private val _liveData = MutableLiveData<String>()
    val liveData = _liveData

    fun select(item: String) {
        _liveData.postValue("$item detail")
    }
}

Activity类

class MenuActivity : BaseActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_menu)
        supportFragmentManager.beginTransaction()
            .add(R.id.fl_menu, MenuFragment.newInstance())
            .add(R.id.fl_detail, DetailFragment.newInstance())
            .commit()
    }
}

MenuFragment

class MenuFragment : BaseFragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()

    companion object {
        fun newInstance() = MenuFragment()
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_menu, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val listView = view.findViewById<ListView>(R.id.listView)
        val data = ArrayList<String>().apply {
            for (i in 0..20) {
                add("menu$i")
            }
        }
        listView.adapter = ArrayAdapter(mContext, android.R.layout.simple_list_item_1, data)
        listView.setOnItemClickListener { parent, view, position, id ->
            sharedViewModel.select(data[position])
        }
    }
}

DetailFragment

class DetailFragment : BaseFragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()

    companion object {
        fun newInstance() = DetailFragment()
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.detail_fragment, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val textView = view.findViewById<TextView>(R.id.textView)
        sharedViewModel.liveData.observe(viewLifecycleOwner, object : Observer<String> {
            override fun onChanged(t: String?) {
                textView.text = t
            }
        })
    }
}

ViewModel与onSaveInstanceState()区别

  • ViewModel:适用于配置变更导致的数据恢复,这是因为内存容量相对较大,可以存储较多的数据。
  • onSaveInstanceState():适用于被系统回收后重建时的数据恢复,这是因为系统回收时应用进程会消亡,因此不能用内存存储而是使用持久化存储,同时这部分数据需要通过Bundle机制传输数据,Bundle缓冲区有大小限制,只能适用于小规模数据。

ViewModel

onSaveInstanceState()

存储方式

在内存中

序列化在磁盘

读写效率

高(内存中访问)

较慢(需要序列化、反序列化操作)

配置更改后,数据是否留存



系统回收后,数据是否留存



使用场景

屏幕旋转等配置更改后保存

Activity异常销毁时才会被调用

存储限制

可存储复杂数据,存储大小受App可用内存影响

只能存储可序列化对象,有存储大小有限制(一般为1M)

代码下载