Architecture Components 知识梳理(3) - ViewModel 示例

一、概述

在第二章的内容中,我们使用ViewModel作为存储LiveData的容器,其实它的功能要比我们看到的更为强大,其应用场景有如下几点。

1.1 应用场景

1.1.1 恢复 re-create 前的数据

在处理屏幕旋转所导致的Activity重启时,我们期望可以保证数据不丢失。在之前,如果我们希望能够恢复这种re-create行为所丢失的数据,那么就需要继承onSaveInstanceState()方法,将数据进行序列化存储在bundle中,之后在onCreate(Bundle savedInstanceState)方法中从bundle中取出数据。

1.1.2 维护异步回调

在日常的业务中,往往需要发起许多异步的操作,并等待其返回的结果。这时候我们就需要去维护这些逻辑,避免在destroy后,回调还一直持有Activity的实例,导致其不能回收,发生内存泄露。

1.1.3 简单的 UI 组件

可以看到,在UI组件中包含了过多的逻辑,但其实UI组件的职责应当是很简单的:展示数据响应用户的行为,其它的逻辑都应当放在UI组件之外。

1.2 目录框架

我们将从下面几个方面对ViewModel做一个初步的了解:

  • 使用ViewModel存储数据,并处理屏幕旋转
  • 了解ViewModel的生命周期
  • 通过ViewModel,处理Fragment之间的交互
  • 使用ViewModel替换loader

二、使用 ViewModel 存储数据,并处理屏幕旋转

Architecture Components 知识梳理(2) - LiveData 示例 中我们演示了如何在ViewModel中存储数据,请大家先看一下前一篇文章中的内容:

/**
 * LiveData 学习 Demo。
 */
public class LiveDataActivity extends AppCompatActivity {

    private Button mBtnRefresh;
    private TextView mTvResult;
    private DataViewModel mViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_data);
        mTvResult = findViewById(R.id.tv_result);
        //1.创建 ViewModel。
        mViewModel = ViewModelProviders.of(this).get(DataViewModel.class);
        Log.d("DataViewModel", "createViewModel, model_address=" + mViewModel);
        //2.添加观察者。
        mViewModel.getWatcher().observeForever(new Observer<List<String>>() {

            @Override
            public void onChanged(@Nullable List<String> strings) {
                Log.d("DataViewModel", "onChanged");
                String tvDisplay = "";
                for (String result : strings) {
                    tvDisplay += (result + "\n");
                }
                //4.数据发生了改变后会回调到这里。
                mTvResult.setText(tvDisplay);
            }
        });
        mBtnRefresh = findViewById(R.id.btn_refresh);
        mBtnRefresh.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                //3.触发加载。
                Log.d("DataViewModel", "mViewModel.load()");
                mViewModel.load();
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d("DataViewModel", "onResume()");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d("DataViewModel", "onPause()");
    }
}

普通的场景在之前已经说明过了,下面我们来看一下在 旋转屏幕 之后会发生什么。首先,我们进入界面,触发加载数据的操作,log如下所示:

触发加载数据的操作

接着,我们旋转屏幕,这将会触发Activity的重启,红色部分是触发重启后的操作:

重启后的操作

这里我们可以发现两个现象:

  • 新的Activity重新走了onCreate方法,但是通过ViewModelProvider.of方法获得的ViewModel和之前仍然是同一个实例。
  • 重启之后,触发了observeronChanged()方法,界面刷新后,又获得了上次保存的数据。

可以看到,并不需要做什么序列化的操作,也不需要进行手动的赋值操作,ViewModel已经为我们处理好了。

三、ViewModel 的生命周期

ViewModel 生命周期

ViewModel的生命周期是和我们通过ViewModelProvider.of操作符传入的UI组件(Activity/Fragment)绑定的,ViewModel的实例将一直存在内存当中,直到UI组件被销毁的时候ViewModel也随之销毁。

对于Activity来说是其finishes的时候,而对于Fragment,则是其detached的时候。

四、在 Fragment 之间共享数据

当我们希望在Fragment之间进行交互或者共享数据的时候,通常都是通过在它们共同的宿主Activity声明接口,再由Activity找到目标Fragment去通知它。

当使用ViewModel后,这一交互过程将会很简单。只需要让两个Fragment都共享同一个ViewModel(也就是说,ViewModelProvider.of(x)传入的是它们共同的宿主Activity),当一方的数据改变后,改变ViewModel中的LiveData,其它观察者就会收到通知,再去进行界面的刷新就可以了。

五、使用 ViewModel 替换 Loader

很久以前我们学习了Loader的实现原理,Loader 知识梳理(1) - LoaderManager初探,它同样可以在屏幕旋转的时候进行自动的数据恢复,并通过CursorLoader在数据发生变化的时候自动去重新加载数据。

使用 Loader 加载数据

现在我们已经可以使用ViewModel+LiveData+Room的方式来替换Loader框架了:

  • ViewModel:负责在UI重建的时候保持数据不丢失。
  • Room:在数据库内容发生变化的时候通知LiveData
  • LiveData:在LiveData的内容发生变化的时候通知UI刷新。
使用 ViewModel 加载数据

六、参考文献

(1) Handling lifecycles with lifecycle-aware components

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

推荐阅读更多精彩内容