在Android中使用LiveData和ViewModel

1、概述

LiveData是一种可观察的数据存储器类,LiveData使用观察者模式,每当数据发生变化时,LiveData会通知 Observer对象,我们可以在这些 Observer 对象中更新UI

ViewModel对象为特定的界面组件(如 Fragment 或 Activity)提供数据,并包含数据处理业务逻辑,会配合LiveData一起使用

接下来,我们会先介绍如果使用LiveData,并编写一个LiveData Demo,接着再结合ViewModel对LiveData Demo的代码进行重构

2、LiveData使用说明

LiveData<T>是一个抽象类,它有2个子类分别是:MutableLiveData<T>MediatorLiveData<T>,在编写代码时,是创建的子类。我们先来看MutableLiveData<T>使用方法,在后面的示例中再介绍如何使用MediatorLiveData<T>

2.1、创建LiveData对象

如果我们要观察的对象类为String,就通过如下代码创建一个MutableLiveData对象

MutableLiveData<String> liveData = new MutableLiveData<>();

2.2、观察LiveData对象

通过observe方法来监听LiveData的数据变化,每当LiveData发生变化时,都会回调onChanged方法,并返回值的内容String s

liveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(String s) {
        Log.i(TAG, "onChanged: " + s);
    }
});

2.3、更新LiveData对象

通过LiveDatasetValue方法,给LiveData赋值,每次赋完值后,都会回调onChanged方法

liveData.setValue("add for test");

当给LiveData赋值为"add for test"时,onChanged会输出日志:

2021-03-20 21:59:21.483 26430-26430/com.example.livedatademo I/LiveDataDemo: onChanged: add for test

3、编写LiveData Demo

我们创建一个Demo,主界面只有一个TextViewTextView用作展示一个数字,这个数字会从59开始显示,然后每隔1秒数字会减1,直到数字变为0。

Demo运行效果

接下来,我们看下不使用LiveData使用LiveData来实现这个Demo的区别

3.1、不使用LiveData

  • 代码说明如下:
    1、在MainActivity增加一个countDown的方法,通过CountDownTimer创建一个倒计时器
    2、new CountDownTimer(1 * 60 * 1000, 1 * 1000)中2个参数的含义:第一个参数表示这个Timer的总时长为60秒;第2个参数表示每隔1秒中会更新一次,并回调onTick方法
    3、每次调用onTick方法,会调用textView.setText(String.valueOf(l / 1000));设置一次TextView的内容

  • 对应代码如下:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "LiveDataDemo";

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        countDown();
    }

    private void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                textView.setText(String.valueOf(l / 1000));
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}
  • 弊端:
    业务逻辑(倒计时的功能)UI逻辑(TextView更新)没有分开,耦合到一起了

3.2、使用MutableLiveData

为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中

Google推荐的使用LiveData的方法
  • 代码说明如下:
    1、在MainActivity中增加一个long类型的MutableLiveData
    2、每次回调onTick方法时,调用liveData.setValue(l);,把最新的值设置给LiveData
    3、调用LivewDataobserve方法,设置一个监听器,每当LiveData的值变化时,会回调onChanged方法,在onChanged中设置TextView的内容

  • 对应代码如下:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "LiveDataDemo";
    private final MutableLiveData<Long> liveData = new MutableLiveData<>();
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        // TODO:为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
        liveData.observe(this, new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
                textView.setText(String.valueOf(aLong / 1000));
            }
        });

        countDown();
    }

    public void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                // TODO:为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
                liveData.setValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

3.3、使用MediatorLiveData

MediatorLiveData可以合并多个LiveData源,只要任何原始的LiveData源对象发生更改,就会触发MediatorLiveData对象的观察者。

例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向MediatorLiveData 对象添加以下源:

  • 与存储在数据库中的数据关联的 LiveData 对象。
  • 与从网络访问的数据关联的 LiveData 对象。

Activity只需观察MediatorLiveData对象即可从这两个源接收更新。接下来,我们会实现这个例子。

3.3.1、监听2个数据源的变化

  • 代码说明如下:
    1、创建1个MediatorLiveData<String>对象

    final MediatorLiveData<String> liveDataMerger = new MediatorLiveData<>();
    

    2、创建2个MutableLiveData:liveData1表示网络数据源,liveData2表示本地数据源

    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();
    

    3、调用MediatorLiveDataaddSource方法,分别将liveData1liveData2加到MediatorLiveData要监听的数据源中

    liveDataMerger.addSource(liveData1, new Observer<String>() {
      @Override
      public void onChanged(String s) {
          Log.i(TAG, "addSource1 onChanged: " + s);
          liveDataMerger.setValue(s);
      }
    });
    liveDataMerger.addSource(liveData2, new Observer<String>() {
      @Override
      public void onChanged(String s) {
          Log.i(TAG, "addSource2 onChanged: " + s);
          liveDataMerger.setValue(s);
      }
    });
    

    4、设置MediatorLiveData的监听,当2个数据源发生变化时,会回调onChanged方法,并将变化的内容展示到TextView

    liveDataMerger.observe(this, new Observer<String>() {
      @Override
      public void onChanged(String s) {
          Log.i(TAG, "liveDataMerger onChanged: " + s);
          textView.setText(s);
      }
    

    });

3.3.2、 编写模拟2个数据源更新的代码

编写一个mergeTes的方法,里面创建了2个Timer,用作模拟网络数据更新和本地数据更新的情况,每隔3秒会往网络数据liveData1设置值、每隔10秒会往本地数据liveData2设置值。对应代码如下:

public void mergeTest() {
    new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
        @Override
        public void onTick(long l) {
            liveData1.setValue("网络有数据更新了" + l/1000);
        }

        @Override
        public void onFinish() {

        }
    }.start();

    new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
        @Override
        public void onTick(long l) {
            liveData2.setValue("本地数据库更新了" + l/1000);
        }

        @Override
        public void onFinish() {

        }
    }.start();
}

3.3.4、MediatorLiveData运行效果

每隔3秒,TextView更新为如下内容:

网络数据有更新

每隔10秒,TextView更新为如下内容:

本地数据有更新

4、ViewModel使用

4.1 创建ViewModel类

创建一个MyViewModel类,继承自ViewModel

import androidx.lifecycle.ViewModel;

public class MyViewMode extends ViewModel  {
}

4.2 在Activity中使用

public class MainActivity extends AppCompatActivity {
    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    }
}

如果遇到new ViewModelProvider(this)报错,就在app/build.gradle中加入依赖implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0"

new ViewModelProvider(this)报错

5、结合ViewModel,重构LiveData Demo

5.1、在ViewModel中增加LiveData

在MyViewModel中增加3个MutableLiveData,分别对应在MainActivity中使用到的。并编写这3个LiveData的get方法

public class MyViewModel extends ViewModel {
    private MutableLiveData<Long> liveData = new MutableLiveData<>();
    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();

    public MutableLiveData<Long> getLiveData() {
        return liveData;
    }

    public MutableLiveData<String> getLiveData1() {
        return liveData1;
    }

    public MutableLiveData<String> getLiveData2() {
        return liveData2;
    }
}

5.2、在ViewModel中添加操作数据的逻辑

countDown、mergeTest这2个方法在操作数据,从MainActivity中把这2个方法copy到 MyViewModel中

public class MyViewModel extends ViewModel {
    private static final String TAG = "MyViewMode";
    ... ...
    public void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                liveData.postValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }

    public void mergeTest() {
        new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
            @Override
            public void onTick(long l) {
                liveData1.postValue("网络有数据更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();

        new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
            @Override
            public void onTick(long l) {
                liveData2.postValue("本地数据库更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

5.3 修改MainActivity中和liveData有关的代码

1、删除countDown,改用viewModel.countDown();

    viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    viewModel.countDown();

2、用viewModel.getLiveData().observe替换liveData.observe

    viewModel.getLiveData().observe(this, new Observer<Long>() {
        @Override
        public void onChanged(Long aLong) {
            textView.setText(String.valueOf(aLong/1000));
        }
    });

3、删除liveData相关代码

经过上述修改后,liveData相关的重构已完成,MainActivity代码如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "LiveDataDemo";

    private TextView textView;

    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();
    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        viewModel.countDown();

       .... ...
    }

5.4、修改MyViewModel

增加liveDataMerger字段,并编写getLiveDataMerger()方法,这个方法里面的内容,是从MainActivity中copy过来的

public class MyViewModel extends ViewModel {
    private static final String TAG = "MyViewMode";
    .... ...
    private MediatorLiveData<String> liveDataMerger;

    public MediatorLiveData<String> getLiveDataMerger() {
        if (liveDataMerger == null) {
            liveDataMerger = new MediatorLiveData<>();
        }
        liveDataMerger.addSource(liveData1, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource1 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        liveDataMerger.addSource(liveData2, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource2 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        return liveDataMerger;
    }
   ... ...
}

5.5、修改MainActivity中和MediatorLiveData有关的代码

1、使用viewModel.getLiveDataMerger().observe替换liveDataMerger.observe

    viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            Log.i(TAG, "liveDataMerger onChanged: " + s);
            textView.setText(s);
        }
    });

2、使用viewModel.mergeTest();替换mergeTest()

    viewModel.mergeTest();

3、删除掉liveDataMerger相关的代码

5.6 重构后MainActivity的代码

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "LiveDataDemo";

    private TextView textView;
    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        viewModel = new ViewModelProvider(this).get(MyViewModel.class);

        viewModel.getLiveData().observe(this, new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
                textView.setText(String.valueOf(aLong / 1000));
            }
        });

        viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "liveDataMerger onChanged: " + s);
                textView.setText(s);
            }
        });


        viewModel.mergeTest();
        // viewModel.countDown();
    }
}

5.7 重构后MyViewModel的代码

public class MyViewModel extends ViewModel {
    private static final String TAG = "MyViewMode";

    private MutableLiveData<Long> liveData = new MutableLiveData<>();
    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();
    private MediatorLiveData<String> liveDataMerger;

    public MediatorLiveData<String> getLiveDataMerger() {
        if (liveDataMerger == null) {
            liveDataMerger = new MediatorLiveData<>();
        }
        liveDataMerger.addSource(liveData1, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource1 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        liveDataMerger.addSource(liveData2, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource2 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        return liveDataMerger;
    }

    public MutableLiveData<Long> getLiveData() {
        return liveData;
    }

    public MutableLiveData<String> getLiveData1() {
        return liveData1;
    }

    public MutableLiveData<String> getLiveData2() {
        return liveData2;
    }

    public void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                liveData.postValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }

    public void mergeTest() {
        new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
            @Override
            public void onTick(long l) {
                liveData1.postValue("网络有数据更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();

        new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
            @Override
            public void onTick(long l) {
                liveData2.postValue("本地数据库更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

6、参考

LiveData 概览
LiveData
MediatorLiveData
ViewModel 概览

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

推荐阅读更多精彩内容