Android框架组件--LiveData的使用

1.前言

LiveData是Google推出的一系列的框架组件的其中一个,它是一个可以被观察的数据持有类,能够感知ActivityFragment等组件的生命周期。

一个观察者去观察LiveData后,当观察者的生命周期处于STARTEDRESUMED状态时(即onStart()onResume()onPause()),LiveData的数据发生变化,则会通知观察者;若观察者处于其他状态,即使LiveData的数据发生变化,也不会发出通知。

正是由于这一特性,因此LiveData可以做到仅在组件处于活跃状态时才进行更新UI的操作。

使用LiveData前需要先了解Lifecycle,如果还不知道Lifecycle,可以看下这篇文章:Android框架组件--Lifecycle的使用

本文主要介绍如何使用LiveData

2.LiveData使用例子

下面来看下如何使用LiveData

3.1 添加依赖

在相应的moudle目录下的build.gradle中添加以下依赖:

dependencies {
    //...
    def lifecycle_version = "1.1.1"
    //仅仅依赖LiveData
    implementation "android.arch.lifecycle:livedata:$lifecycle_version"
}

3.2 创建LiveData对象

google官网提倡LiveData配合ViewModel一起使用。为了专注LiveData,这里先不用ViewModel,后面再补充说明如何跟ViewModel一起使用。直接看例子:

public class TestModel {
    private MutableLiveData<String> status;

    public MutableLiveData<String> getStatus() {
        if (status == null)
            status = new MutableLiveData<>();
        return status;
    }
}

MutableLiveData继承自LiveData,表示可变数据。这里创建一个保存String类型数据的LiveData

3.3 观察LiveData对象

通常,会在ActivityonCreate()方法中开始对LiveData的观察:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestModel mTestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        //创建一个观察者去更新UI
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //创建TestModel对象
        mTestModel = new TestModel();
        //观察LiveData对象
        //这里的this指的是LifecycleOwner,即LiveData会跟生命周期联系起来
        mTestModel.getStatus().observe(this, statusObserver);
    }
}

观察者可以通过observe()方法对LiveData进行监听,observe()方法需要传递一个LifecycleOwner参数进去,这表示LiveData会跟生命周期联系起来。

另外还有一个observeForever()方法,只需传递一个观察者进去就行,这意味着它跟生命周期没有任何关系,可以持续的观察,只要数据发生变化,都会通知观察者回调onChanged()

3.4 更新LiveData对象

MutableLiveData提供了setValue(T)(主线程使用)和postValue(T)(子线程使用)两个方法来修改数据。LiveData并没有提供类似方法。当调用MutableLiveDatasetValue(T)postValue(T)方法后,ObserveronChanged()方法将会被回调,从而实现更新UI的操作。

注意,这是在观察者处于STARTEDRESUMED状态时,LiveData才会通知观察者数据变化;当观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。

当组件的状态为DESTROYED时会自动移除观察者,这样ActivityFragment就可以安全地观察LiveData而不用担心造成内存泄露。

我们来看个完整的例子:

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestModel mTestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        //创建一个观察者去更新UI
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //创建TestModel对象
        mTestModel = new TestModel();
        //观察LiveData对象
        //这里的this指的是LifecycleOwner,即LiveData会跟生命周期联系起来
        mTestModel.getStatus().observe(this, statusObserver);

        mTestModel.getStatus().setValue("onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        mTestModel.getStatus().setValue("onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        mTestModel.getStatus().setValue("onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        mTestModel.getStatus().setValue("onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        mTestModel.getStatus().setValue("onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTestModel.getStatus().setValue("onDestroy");
    }
}

一个完整的生命周期走下来,其输出结果为:

07-26 22:03:34.798 20995-20995/com.test.start D/test: onChanged: onStart
07-26 22:03:34.801 20995-20995/com.test.start D/test: onChanged: onResume
07-26 22:03:36.456 20995-20995/com.test.start D/test: onChanged: onPause

可以看到,只在onStart()onResume()onPause()时观察者才会收到数据更新的通知,其他状态下即使更新了数据也不会收到通知。

3. LiveData配合ViewModel使用

下面来看下跟ViewModel是如何一起使用的。

3.1 添加依赖

使用ViewModel还需添加ViewModel的依赖:

dependencies {
    //...
    def lifecycle_version = "1.1.1"
    //ViewModel 和 LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}

3.2 创建ViewModel

public class TestViewModel extends ViewModel {
    private MutableLiveData<String> status;

    public MutableLiveData<String> getStatus() {
        if (status == null)
            status = new MutableLiveData<>();
        return status;
    }
}

继承ViewModel后即可。

3.3 观察并更新LiveData对象

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestViewModel mTestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };

//        mTestModel = new TestModel();

        //改为通过ViewModel来创建对象
        mTestModel = ViewModelProviders.of(this).get(TestViewModel.class);
        mTestModel.getStatus().observe(this, statusObserver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //更新数据
        mTestModel.getStatus().setValue("onResume");
    }
}

ViewModel跟普通Model流程都是一样的,只是创建对象时不一样而已。
Google建议LiveData配合ViewModel一起使用。

4. 扩展LiveData

除了使用MutableLiveData外,我们还可以通过继承LiveData类来扩展一些功能。
比如:只有观察者观察了LiveData的数据时,才开始进行进行获取数据的操作,这样可以节省资源,代码如下所示:

4.1 继承LiveData

public class TestLiveData extends LiveData<String> {
    private static TestLiveData sInstance;
    private LocationUtil mLocationUtil;

    //设计为单例模式
    public static TestLiveData getInstance() {
        if (sInstance == null) {
            sInstance = new TestLiveData();
        }
        return sInstance;
    }

    private TestLiveData() {
        //创建一个获取位置的对象
        mLocationUtil = new LocationUtil();
    }

    @Override
    protected void onActive() {
        //开始获取位置信息
        mLocationUtil.start(mLocationListener);
    }

    @Override
    protected void onInactive() {
        //停止获取位置信息
        mLocationUtil.stop();
    }
    
    //创建一个位置监听器
    private LocationListener mLocationListener = new LocationListener() {
        @Override
        public void onReceiveLocation(String location) {
            //接受到位置信息后,更新数据
            setValue(location);
        }
    };
}

LiveData设计成单例模式后,可以在多个ActivityFragmentService之间共享它。

下面我们来重点关系一下LiveData的三个方法:onActive()onInactive()setValue()

  • onActive():当有一个处于活跃状态的观察者监听LiveData时会被调用,这表示开始获取位置信息。
  • onInactive():当没有任何处于活跃状态的观察者监听LiveData时会被调用。由于没有观察者在监听了,所以也没必要继续去获取位置信息了,这只会消耗更多的电量等等,因此就可以停止获取位置信息了。
  • setValue():更新LiveData的值,并通知到观察者。

4.2 使用自定义的LiveData

下面来看下怎么使用这个自定义的LiveData

public class TestActivity extends AppCompatActivity {

    private static final String TAG = "test";

    private TextView mTextView;
    private TestLiveData mTestLiveData;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.test_activity);
        mTextView = findViewById(R.id.tv_test);

        initVariable();
    }

    private void initVariable() {
        final Observer<String> statusObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                Log.d(TAG, "onChanged: " + newName);
                mTextView.setText(newName);
            }
        };
        //通过单例获取对象
        mTestLiveData =TestLiveData.getInstance();
        //同样是去观察LiveData
        mTestLiveData.observe(this, statusObserver);
    }
}

跟之前的一样,还是通过一个观察者去监听这个mTestLiveData对象。

5. LiveData变换

LiveData变换主要有两种变换:mapswitchMap,都是Transformations类提供的。

5.1 map变换

public class TestViewModel extends ViewModel {
    private MutableLiveData<Integer> mNumLiveData= new MutableLiveData<>();
    
    //通过Transformations.map()将Integer类型的值转换为String类型
    private LiveData<String> mStrLiveData = Transformations.map(mNumLiveData, num -> num + "");

    public MutableLiveData<Integer> getNumLiveData() {
        return mNumLiveData;
    }
    
    public LiveData<String> getStrLiveData() {
        return mStrLiveData;
    }
}

map变换可以直接修改返回的值和类型。

5.2 switchMap变换

public class TestViewModel extends ViewModel {

    private MutableLiveData<Integer> mNumLiveData = new MutableLiveData<>();
    
    //switchMap变换
    private LiveData<String> mNameLiveData = Transformations.switchMap(mNumLiveData, num -> getName(num));
    
    //返回一个LiveData
    private LiveData<String> getName(Integer num) {
        MutableLiveData<String> liveData = new MutableLiveData<>();
        liveData.setValue(num + "a");
        return liveData;
    }

    public MutableLiveData<Integer> getNumLiveData() {
        return mNumLiveData;
    }

    public LiveData<String> getNameLiveData() {
        return mNameLiveData;
    }
    
}

switchMap变换需要返回一个LiveData对象,这就是跟map变换的区别。

6. 合并多个LiveData数据源

如果有多个LiveData,可以使用MediatorLiveData来合并这些LiveData,一旦其中一个LiveData发生变化,MediatorLiveData都会通知观察者。比如:一个UI界面,依赖于网络数据和数据库,因此就会存在两个LiveData。使用MediatorLiveData将两个LiveData合并后,UI界面只需要观察一个MediatorLiveData即可。当其中一个LiveData数据发生变化时都会通知UI界面去更新。

6.1 MediatorLiveData使用例子

MediatorLiveData的简单使用如下所示:

        LiveData liveData1 = ...;
        LiveData liveData2 = ...;

        MediatorLiveData liveDataMerger = new MediatorLiveData<>();

        liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
        liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

然后在UI界面直接观察这个liveDataMerger即可。

6.2 MediatorLiveData的方法

MediatorLiveData相比于LiveData,主要是多了以下两个方法:

  • addSource():添加源LiveData,并且开始监听给定的源LiveData,当源LiveData的数据发生变化时,观察者的onChanged()方法将会被调用,前提是MediatorLiveData处于活跃状态。
  • removeSource():移除LiveData

再来看个例子:

liveDataMerger.addSource(liveData1, new Observer() {
      private int count = 1;

      @Override public void onChanged(@Nullable Integer s) {
          count++;
          liveDataMerger.setValue(s);
          if (count > 10) {
              liveDataMerger.removeSource(liveData1);
          }
      }
 });

监听liveData1的数据,当监听了10次之后,移除对liveData1监听。

6.3 使用Transformations实现自定义变换

MediatorLiveData除了可以合并数据外,实际上还可以用来实现自定义的变换方法,上面Transformationsmap()switchMap()如果不能满足变换需求的话,那么就可以用MediatorLiveData来实现。

7. LiveData优点

使用LiveData具有以下优点:

  • 1.确保UI跟数据状态一致
    组件处于活跃状态时,当数据发生变化,LiveData会通知观察者去更新UI,从而使得他们保持一致。

  • 2.没有内存泄露
    由于观察者绑定到了Lifecycle对象上,因此在Lifecycle被销毁后,观察者会被自行清理掉。

  • 3.停止Activity不会造成崩溃
    如果Activity处于非活跃的状态,比如Activity在后台时,那么它不会接受到LiveData的数据变更事件。

  • 4.无需手动处理生命周期
    UI组件仅仅需要观察相应的数据即可,无需手动去停止或恢复观察。因为LiveData会自动管理这所有的,它在观察时能够意识到相关的生命周期状态变化。

  • 5.始终保持最新数据
    如果生命周期处于不活跃的状态,那么当它变为活跃状态时将会收到最新的数据。比如:后台Activity变为前台时将会收到最新的数据。

  • 6.适当的配置更改
    如果由于配置更改而重新去创建ActivityFragment,那么会立即接收最新的可用数据。

  • 7.资源共享
    你可以继承LiveData并使用单例模式来扩展系统的服务,这样你就可以共享它。这个自定义的LiveData对象只需连接一次系统服务,其他需要这些资源的观察者只需观察这个LiveData即可。可以参看第4小节:扩展LiveData。

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

推荐阅读更多精彩内容