LiveData使用

一、前言:

1. 简介:

1、LiveData的简介

LiveData是一种类,持有可被观察的数据。
LiveData是一种可感知生命周期的组件,意味着该组件重视其他app组件的生命周期,如Activity、Fragment、Service
该组件能确保,仅仅在Activity\Fragment\Service等组件都处于活跃的生命周期状态的时候,才去更新app组件。

2、LiveData只有当观察者的生命周期处于活跃状态时才会去通知观察者。

实现了Observer类的观察者,可以注册监听LiveData
活跃状态就是指处于STARTED或者RESUMED状态
处于非活跃的观察者,LiveData不会去通知这些观察者

3、可以注册一种观察者, 该观察者与LifecycleOwner对象(如:Activity、Fragment)相关联。

在对应的Lifecycle Object处于DESTORYED状态时,会自动解除LiveData和该观察者的注册关系

4、在Activity、Fragment中这种自动解除注册的特性非常有用

Activity、Fragment不用担心会出现内存泄露
在Activity、Fragment销毁时,LiveData会自动解除其注册关系

2. 优势:

5、LiveData能确保UI和数据状态相符

因为是观察者模式,LiveData会在生命周期状态改变时,通知观察者
可以在观察者对象中进行UI的更新操作

6、LiveData没有内存泄露

观察者和Lifecycle对象绑定,能在销毁时自动解除注册

7、LiveData不会给已经停止的Activity发送事件

如果观察者处于非活跃状态,LiveData不会再发送任何事件给这些Observer对象

8、LiveData能确保不再需要手工对生命周期进行处理

UI组件仅仅需要对相关数据进行观察
LiveData自动处理生命周期状态改变后,需要处理的代码。

9、LiveData能保证数据最新

一个非活跃的组件进入到活跃状态后,会立即获取到最新的数据
不用担心数据问题

10、LiveData在横竖屏切换等Configuration改变时,也能保证获取到最新数据

例如Acitivty、Fragment因为屏幕选装导致重建, 能立即接收到最新的数据

11、LiveData能资源共享

如果将LiveData对象扩充,用单例模式将系统服务进行包裹。这些服务就可以在app中共享。
只需要LiveData和系统服务connect,其他观察者只需要监视LiveData就能获取到这些资源

二、使用LiveData

1、LiveData与MutableLiveData区别

LiveData与MutableLiveData的其实在概念上是一模一样的.唯一几个的区别如下:

  1. MutableLiveData的父类是LiveData
  2. LiveData在实体类里可以通知指定某个字段的数据更新.
  3. MutableLiveData则是完全是整个实体类或者数据类型变化后才通知.不会细节到某个字段

2、LiveData有几种使用方式:

  1. 使用LiveData对象
  2. 继承LiveData类

3、为什么将LiveData放置到ViewModel中,而不放到activity或者fragment中?

  1. 避免fragment和activity的代码臃肿
  2. 将LiveData和特定的activity/fragment解耦,能够在configuration改变的时候,LiveData依然存活。

4、在App组件的哪个生命周期适合观察LiveData对象?为什么?

  1. app组件的onCreate()方法
  2. 不适合在onResume()等方法中,可能会调用多次
  3. 能确保组件能尽可能快的展示出数据。只要app组件处于启动状态(STARTED)就会立即接收到LiveData对象中的数据—前提是已经监听了LiveData

5、ViewModelProviders为什么找不到?

引用的版本太老了,需要新的Lifecyle扩展库(目前可以用的最新版)
//viewMouble使用
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

6、创建LiveData实例

Android文档中建议LiveData配合ViewModel使用更加哦,其实呢,你也可以不使用ViewModel,但是一定要做到LiveData中保存的数据和组件分离,至于原因,前面我们已经提到过了。下面是在ViewModel中创建LiveData实例的例子:

public class NameViewModel extends ViewModel{
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;
    // Create a LiveData with a String list
    private MutableLiveData<List<String>> mNameListData;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<>();
        }
        return mCurrentName;
    }

    public MutableLiveData<List<String>> getNameList(){
        if (mNameListData == null) {
            mNameListData = new MutableLiveData<>();
        }
        return mNameListData;
    }
}

在NameViewModel中创建了两个MutableLiveData(MutableLiveData是LiveData的子类)实例,分别存储当前姓名、姓名列表;两个实例通过NameViewModel中的getter方法得到。

7、创建Observer对象,添加观察者

public class LiveDataFragment extends Fragment {
    private static final String TAG = "LiveDataFragment";
    private NameViewModel mNameViewModel;
    @BindView(R.id.tv_name)
    TextView mTvName;

    public static LiveDataFragment getInstance(){
        return new LiveDataFragment();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNameViewModel = ViewModelProviders.of(this).get(NameViewModel.class);
        // 订阅LiveData中当前Name数据变化,以lambda形式定义Observer
      mNameViewModel.getCurrentName().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String name) {
                mTvName.setText(name);
                Log.d(TAG, "currentName: " + name);
            }
        });
       // 订阅LiveData中Name列表数据变化,以lambda形式定义Observer
      mNameViewModel.getNameList().observe(this, new Observer<List<String>>() {
            @Override
            public void onChanged(List<String> nameList) {
                for (String item : nameList) {
                    Log.d(TAG, "name: " + item);
                }
            }
        });
    }


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.layout_livedata, container, false);
        ButterKnife.bind(this, view);
        return view;
    }

}

在onCreate()方法中通过LiveData.observe()方法添加观察者,当数据变化时会通过回调方法通知观察者,在lambda表达式中更新当前姓名和打印姓名列表。

8、更新LiveData中的数据

在上面我们已经订阅了LiveData数据变化,现在我们看下如果LiveData数据变化时,上面的lambda表达式中是否会受到更新的通知。我们在LiveDataFragment中增加两个按钮来改变LiveData中的数据。

@OnClick({R.id.btn_change_name, R.id.btn_update_list})
void onClicked(View view){
    switch (view.getId()){
        case R.id.btn_change_name:
            mNameViewModel.getCurrentName().setValue("Jane");
            break;
        case R.id.btn_update_list:
            List<String> nameList = new ArrayList<>();
            for (int i = 0; i < 10; i++){
                nameList.add("Jane<" + i + ">");
            }
            mNameViewModel.getNameList().setValue(nameList);
            break;
    }
}

代码很简单,在两个按钮的点击事件中通过LiveData.setValue()方法来改变LiveData中保存的数据。当点击这两个按钮的时候,我们会发现在onCreate()方法中会收相应到数据改变的回调。

9、继承LiveData类

除了直接使用LiveDatad对象外,我们还可以通过集成LiveData类来定义适合特定需求的LiveData。下面继承LiveData类的例子,验证下LiveData的其中一个优点——资源共享。

package com.hao.architecture;

import android.arch.lifecycle.LiveData;

import java.math.BigDecimal;

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager stockManager;

    public StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            // 更新LiveData并且通知所有活跃的观察者
            setValue(price);
        }
    };

    @Override
    protected void onActive() {
        // 具有活跃的观察者时调用
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        // 没有任何活跃的观察者时调用
        stockManager.removeUpdates(listener);
    }
}
  • onActive(),此方法是当处于激活状态的observer个数从0到1时,该方法会被调用。
  • onInactive() ,此方法是当处于激活状态的observer个数从1变为0时,该方法会被调用。

10、改变LiveData数据

LiveData提供了两种改变数据的方法:setValue()和postValue()。
区别是:

  • setValue()要在主线程中调用,
  • postValue()既可在主线程也可在子线程中调用。

我们先看setValue()方法的具体实现:

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue"); //判断当前线程是否是主线程,不是主线程就抛出异常
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

再看下postValue()方法的具体实现:

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    // 会在主线程中执行  mPostValueRunnable中的内容。
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); 
}

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 在主线程中调用setValue()方法
        setValue((T) newValue); 
    }
};

postValue()方法通过ArchTaskExecutor实现在主线程中执行mPostValueRunnable对象中的内容,而在mPostValueRunnable中最终会调用setValue()方法来实现改变LiveData存储的数据。

11、添加观察者

LiveData提供了两种添加观察者的方法:observeForever()、observe()。

  • observeForever()
@MainThread
public void observeForever(@NonNull Observer<T> observer) {
    observe(ALWAYS_ON, observer);
}

从方法的命名,我们也能对它的功能略知一二,通过observeForever()添加观察者,观察者会一直受到数据的变化回到,而不是在组件处于STARTED和RESUMED状态下才会收到,因为这是LifecycleOwner对象就不再是组件了,而是ALWAYS_ON;另外通过该方法添加观察者后,要手动调用removeObserver()方法来停止观察者接收回调通知。observeForever()方法体很简单,调用了observe()方法,传入的一个参数是ALWAYS_ON常量,重点看下ALWAYS_ON常量是个啥东东。

private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {

    private LifecycleRegistry mRegistry = init();

    private LifecycleRegistry init() {
        LifecycleRegistry registry = new LifecycleRegistry(this);
        registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
        registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
        registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
        return registry;
    }

    @Override
    public Lifecycle getLifecycle() {
        return mRegistry;
    }
};

ALWAYS_ON是LifecycleOwner常量,在init方法中会初始化Lifecycle的生命周期状态,完了之后,就没有改变过Lifecycle的生命周期状态了,这也就是为什么通过observeForever()添加观察者是,当数据改变时不管组件处于什么状态都会收到回调的原因,除非手动将观察者移除。

  • observe()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    //将LifecycleOwner对象和Observer对象封装成LifecycleBoundObserver对象。
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // mObservers可以理解成一个类似Map的容器,putIfAbsent()方法是判断容器中的observer(key)
    // 是否有已经和wrapper(value)关联,如果已经关联则返回关联值,否则关联并返回wrapper。
    LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && existing.owner != wrapper.owner) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper); //条件LifecycleOwner的生命周期观察者
}

该方法也比较简单,主要逻辑都在注释中说明了,就不再赘述了。


链接:https://www.jianshu.com/p/2fa0aa513a32

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