ViewModel

一、ViewModel要解决的问题

在ViewModel出现之前,无论是MVP、MVC还是MVVM,我们都需要自己处理数据的状态保存问题。因为展示给用户的页面(Activity、Fragment)随时可能会被系统销毁,比如用户旋转手机屏幕或者手机内存不足等等。等页面重建时,我们需要重新获取数据进行展示。对于简单的数据,我们可以通过使用onSaveInstanceState()来恢复,而不适合大量的列表数据。同时,我们获取数据通常时异步请求的,如果这些调用返回前,发生了销毁。我们需要进行额外的取消清理工作,防止内存泄漏。
这些逻辑的处理往往繁琐并且重复,所以Google官方推出了ViewModel来解决这个问题。ViewModel负责为界面准备数据,并在更改配置期间自动保留ViewMode对象,以便它们存储的数据立即可供下一个Activity或Fragment实例使用。

二、实现ViewModel

class MyViewModel : ViewModel {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData().alse {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<Users>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users
    }
}
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        val model = ViewModelProviders.of(this)[MyViewModel::class.java]
        model.getusers().observe(this, Observer<List<User>>{ users ->
            // update UI
        })
    }
}

如果重新创建了该Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同。当所有者Activity销毁时,框架会调用ViewModel的onCleared()方法,以便它可以清理资源。
ViewModel对象存在的时间比视图或LifecycleOwners的特定实例存在的时间更长。这还意味着,您可以更轻松地编写覆盖ViewModel的测试,因为它不了解视图和Lifecycle对象。ViewModel对象可以包含LifecycleObservers,如LiveData对象。但是,ViewModel对象绝不能观察对生命周期感知型可观测对象(如LiveData对象)的更改。如果ViewModel需要Application上下文(例如,为了查找系统服务),它可以扩展AndroidViewModel类并设置用于接收Application的构造函数,因为Application类会扩展Context。

三、ViewModel的生命周期

ViewModel对象存在的时间范围是获取ViewModel时传给ViewModelProvider的Lifecycle。ViewModel将一直停留在内存中,直到限定其存在时间范围的Lifecycle永久消失:对于Activity,是在Activity Finish的时候。对于Fragment,是在Fragment Destroy的时候。

image.png

您通常在系统首次调用Activity对象的onCreate()方法时请求ViewModel。系统可能会在Activity的整个生命周期内多次调用onCreate(),如在旋转设备时。ViewModel存在的时间范围是从您首次请求ViewModel直到Activity完成并销毁。

四、在Fragment之间共享数据

Activity中的两个或更多Fragment需要互相通信时一种很常见的情况。想象一下主从Fragment的常见情况,假设您有一个Fragment,在该Fragment中,用户从列表中选择一项,还有另一个Fragment,用于显式选定的内容。这种情况不太容易处理,因为这两个Fragment都需要定义某种接口描述,并且所有者Activity必须将两者绑定在一起。此外,这两个Fragment都必须处理另一个Fragment尚未创建或不可见的情况。
可以使用ViewModel对象解决这一常见的难点。这两个Fragment可以使用其Activity范围共享ViewModel来处理此类通信,如以下示例代码所示:

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

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

class MasterFragment :  Fragment() {
    private lateinit var itemSelector : Selector
    private lateinit var model : ShareViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this)[SharedViewModel::class.java]
        } ?: throw Exception("Invalid Activity")
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }

    class DetailFragment : Fragment() {
        private lateinit var model: SharedViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            model = activity?.run {
                ViewModelProviders.of(this)[SharedViewModel::class.java]
            } ?: throw Exception("Invalid Activity")
            model.selected.observe(this, Observer<Item> {
                // Update the UI
            })
        }
    }
}

请注意,这两个Fragment都会检索包含它们的Activity。这样,当两个Fragment各自获取ViewModelProvider时,它们会收到相同的ShardedViewModel实例(其范围限定为该Acitivity)
此方法具有以下优势:

  • Activity不需要执行任何操作,也不需要对此通信有任何了解。
  • 除了ShareViewModel约定之外,Fragment不需要互相了解。如果其中一个Fragment消失,另一个Fragment将继续照常工作。
  • 每个Fragment都有自己的生命周期,而不受另一个Fragment的生命周期的影响。如果一个Fragment替换另一个Fragment,界面将继续工作而没有任何问题。

1、Android ViewModel,再学不会你砍我

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容