Android Jetpack - ViewModel

2021-09-15  本文已影响0人  teletian

◾︎简介

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。

举个例子:在 MVP 中,Activity/Fragment 调用 P 层的异步请求。由于 P 层要持有 Activity/Fragment 的引用用于回调接口,当 Activity/Fragment 销毁后,造成内存泄露。为此,必须做大量的工作去管理异步请求。

而 ViewModel 不持有 Activity/Fragment 的引用,不存在上述问题。ViewModel 的 UI 更新是通过观察者模式(LiveData)实现的。

ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

当屏幕旋转时,为了避免数据丢失,通常会在 onSaveInstanceState() 中保存数据,然后在 onCreate() 的 Bundle 中恢复数据。但是这种方法有限制,数据必须支持序列化和反序列化,而且数据不能太大。

而 ViewModel 在 Activity 销毁重建时,会关联到新的 Activity,不需要手动保存再恢复。ViewModel 保存的数据格式和大小也没有像 Bundle 的限制。

本文代码使用 Kotlin 讲解,若需查看 Java 代码写法,请参考文末 Sample

◾︎添加依赖

ViewModel 一般和 LiveData 一起使用。

def lifecycle_version = "2.3.1"

// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

◾︎使用

定义 ViewModel

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MyViewModel : ViewModel() {

    private val news: MutableLiveData<String> by lazy {
        MutableLiveData<String>().also {
            fetchNews()
        }
    }

    fun getNews(): LiveData<String> {
        return news
    }

    private fun fetchNews() {
        Thread {
            Thread.sleep(3000)
            news.postValue("Completed!")
        }.start()
    }
}

使用 ViewModel

import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val model = ViewModelProvider(this)[MyViewModel::class.java]
        model.getNews().observe(this) { msg ->
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
        }
    }
}

◾︎ViewModel 的生命周期

ViewModel 的生命周期取决于传入 ViewModelProvider 的 ViewModelStoreOwner,也就是 Activity/Fragment。ViewModel 一直存在内存中,直到 Activity 完成时,Fragment 分离时。

所以,屏幕旋转后,ViewModel 依然存在在内存中。屏幕旋转时,ViewModel 的生命周期如下:


◾︎在 Fragment 之间共享数据

比如一个界面左边是列表 Fragment,右边是详细信息 Fragment。点了列表 Fragment 某一项需要把该项的信息传递给详细信息 Fragment。
先定义一个用于共享数据的 ViewModel

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

然后在两个 Fragment 中分别获取 ViewModel

class ListFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            model.select(item)
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
        })
    }
}

这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 SharedViewModel 实例。
有了 ViewModel,Activity 不需要执行任何操作就可以实现 Fragment 间的数据共享。而且 Fragment 之间也相互独立,就算其中一个 Fragment 消失了,另一个 Fragment 也照样正常工作。

◾︎Sample

Kotlin 版
Java 版

上一篇 下一篇

猜你喜欢

热点阅读