Android Jetpack—LiveData和数据倒灌

7ea8cfa0dea64306aef3d792bde1292a.jpg

 1.LiveData

LiveData是Android Jetpack包提供的一种可观察的数据存储器类,它可以通过添加观察者被其他组件观察其变更。不同于普通的观察者,LiveData最重要的特征是它具有生命周期感知能力,它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保LiveData仅更新处于活跃生命周期状态的应用组件观察者。如在Activity中如果数据更新了但Activity已经处于destroy状态,则LiveData就不会通知Activity(observer)了。

此外,LiveData还有许多优点:

①它可以做到在组件处于激活状态的时候才会回调相应的方法,从而刷新相应的UI,不用担心发生内存泄漏。

②当config导致activity重新创建的时候,不需要手动去处理数据的储存和恢复。它已经帮我们封装好了。

③当Activity不是处于激活状态的时候,如果想 Livedata调用setValue之后立即回调obsever的onChange方法,而不是等到Activity处于激活状态的时候才回调obsever的onChange方法,可以使用observeForever方法,但是必须在onDestroy的时候removeObserver。(如果你想无论页面处于何种生命周期,setValue或postValue之后立刻回到数据。那么可以使用observerForever()方法,使用起来与observer()没有太大差别. 因为AlwaysActiveObserver没有实现GenericLifecycleObserver 接口,不能感应生命周期。但是需要注意的是,在用完之后,一定要记得在onDestroy()方法中调用removeObserver()方法来停止对LiveData的观察,否则LiveData会一直处于激活状态,Activity则永远不会被系统自动回收,会造成内存泄露。

用它可以实现一个事件总线来代替event bus。)

回想一下,在项目中是不是经常会碰到这样的问题:当网络请求结果回来的时候,经常需要判断Activity或者Fragment是否已经Destroy, 如果不是destroy才更新UI。而如果使用Livedata的话,因为它是在Activity处于onStart或者onResume的状态时,它才会进行相应的回调,因而可以很好得处理这个问题,不必写一大堆的 activity.isDestroyed()。

LiveData有两个子类:MutableLiveData和MediatorLiveData。MutableLiveData针对单个需要观察的数据进行了封装,而MediatorLiveData则可以观察其它的LiveData。开发过程中通常使用LiveData的子类,而不是去继承LiveData。

LiveData通常会配合ViewModel一起使用,ViewModel负责触发数据的更新,更新会通知到LiveData,然后LiveData再通知活跃状态的观察者。当然,LiveData也可以单独使用。

扫描二维码关注公众号,回复: 15168536 查看本文章

比如MutableLiveData的使用方法:

private void liveDataTest(){

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

    mutableLiveData.observe(this, new Observer<String>() {

        @Override

        public void onChanged(String value) {

           //如果数据有变化的话,这里会接收到通知,可以在这里可以做一些反应,比如更新界面

        }

     });

    mutableLiveData.setValue("我的值变化了");

}

分为三步:

①创建对应数据类型的LiveData

②添加观察者与Lifecycle建立联系

③发送对应的数据进行更新

2.LiveData使用

①首先引入依赖

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

②布局文件activity_live_data_test.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    <TextView

        android:id="@+id/tv_text"

        android:layout_width="match_parent"

        android:layout_height="50dp"/>

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

        <ImageButton

            android:id="@+id/img_button_add"

            android:layout_width="70dp"

            android:layout_height="70dp"/>

        <ImageButton

           android:id="@+id/img_button_subtract"

            android:layout_width="70dp"

            android:layout_height="70dp"   />

    </LinearLayout>

</LinearLayout>

③创建ViewModelWithLiveData类继承于ViewModel,在这个类里面声明变量

public class ViewModelWithLiveData extends ViewModel {

    private MutableLiveData<Integer> LikedNumber;  

    public MutableLiveData<Integer> getLikedNumber() {

        if (LikedNumber == null) {

            //LikedNumber是对象类型,不是基本数据类型,所以要保证变量不是空的

            LikedNumber = new MutableLiveData<>();       

            LikedNumber.setValue(0);  //初始化为0

        }

        return LikedNumber;

    }

    public void addLikedNumber(int n) {

        LikedNumber.setValue( LikedNumber.getValue() + n);

    }

}

④给viewModelWithLiveData里面的变量添加一个观察,点击按钮实现+1、-1操作

public class LiveDataTestActivity extends AppCompatActivity {

    private ViewModelWithLiveData viewModelWithLiveData;

    private TextView tv_text;

    private ImageButton img_button_add;

    private ImageButton img_button_subtract;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView( R.layout.activity_live);

        viewModelWithLiveData = new ViewModelProvider(this).get(ViewModelWithLiveData.class);

        //给viewModelWithLiveData里面的变量添加一个观察,观察它自己,如果数据发生变化则呼叫下面的onChange函数。observe()的第一个参数要求是具有LifeCycle管理功能的一些对象,比如Activity

        viewModelWithLiveData.getLikedNumb er().observe(this, new Observer<Integer>() {

          @Override

          public void onChanged(Integer integer) { //当LiveData数据发生改变时回调这个函数

              tv_text.setText( String.valueOf(integer));

            }

        });

        img_button_add.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                viewModelWithLiveData.addLikedN umber(1);

            }

        });

        img_button_subtract.setOnClickListener( new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                viewModelWithLiveData.addLikedN umber(-1);

            }

        });

    }

}

ac6e22946cbb422a94eec0ed51489137.gif

并且,旋转屏幕,切换系统语言等配置时,数据不会丢失。

LiveData的好处:

①监听与其绑定的界面的生命周期。因此,使用LiveData就不需要手动管理它的生命周期了。

②组件能及时响应LiveData的数据变化,组件总能拿到LiveData的最新数据。当然,被绑定的组件响应LiveData是有一定的前提的,那就是LiveData数据发生变化,且组件处于活跃状态。也就是说,LiveData数据即使发生了变化,也不一定会响应onChanged函数,因为它必须要求LiveData数据所在的界面处于活跃状态,才会响应onChanged函数。

③生命周期自动解绑,它能够在组件销毁的时候自动解绑,大大降低了应用产生内存泄露的概率。

注:LiveData确实能解决内存泄漏问题,但是如果使用不当,其实还是会出现内存泄漏的。例如,有一个Activity,Activity包含了一个Fragment,Fragment中有一个LiveData,因此Fragment引用了LiveData。然后LiveData通过observe方法把Activity当作owner进行了绑定,那么这时候,LiveData的生命周期将和Activity一样。如果这时候因为某种原因,Fragment被销毁了,那么LiveData将不会被销毁,因为它被Activity引用着。LiveData本该回收却无法被回收,那么LiveData就发生内存泄漏了。

3.LiveData源码

①observe方法

LiveData使用observe()方法监听与其绑定的界面的生命周期。

LiveData.java:

@MainThread

public void observe(LifecycleOwner owner, Observer<? super T> observer) {

    assertMainThread("observe"); //只能在主线程调用这个方法

    if (owner.getLifecycle().getCurrentState() == DESTROYED) {

        return; //如果owner的生命周期已经是DESTROYED状态,则不再往下执行

    }

    //将外部传进的observer对象封装成一个LifecycleBoundObserver对象

    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

    //将observer和wrapper分别作为key和value存放到map中

    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

    if (existing != null && !existing.isAttachedTo(owner)) {

        throw new IllegalArgumentException( "Cannot add the same observer with different lifecycles"); //同一个观察者不能观察不同的LifecycleOwner

    }

    if (existing != null) {

        return;

    }

    owner.getLifecycle().addObserver(wrapper);

}

首先,将外部传进的observer对象封装成LifecycleBoundObserver对象,对象名称为wrapper,其实LifecycleBoundObserver就是一个可以监听生命周期的类,可以看看LifecycleBoundObserver类的构造方法:

LiveData.LifecycleBoundObserver.java:

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {

    final LifecycleOwner mOwner;

    LifecycleBoundObserver(LifecycleOwner owner, Observer<? super T> observer) {

        super(observer);

        mOwner = owner;

    }

    // …………………………

}

public interface GenericLifecycleObserver extends LifecycleEventObserver { }

public interface LifecycleEventObserver extends LifecycleObserver {

    void onStateChanged(LifecycleOwner source, Lifecycle.Event event);

}

可以看到,LifecycleBoundObserver实现了GenericLifecycleObserver接口,而实际上这个接口最终继承自LifecycleObserver接口,LifecycleObserver就是使用lifecycle的时候调用addObserver时需要传递的参数。所以,通过new LifecycleBoundObserver( owner, observer)方法得到的wrapper实际上就是一个LifecycleObserver。

继续回到LiveData的observe方法,后面调用了mObservers.putIfAbsent(observer, wrapper)这行代码,将observer作为key,wrapper作为value存放到mObservers中,并且会返回一个ObserverWrapper对象。putIfAbsent方法会判断,如果value已经存在,则返回,否则返回null。所以如果返回null就说明以前没有添加过这个观察者,于是就将LifecycleBoundObserver作为owner生命周期的观察者。

mObservers是一个由链表实现的支持键值对存储的数据结构,同时,它支持在遍历的过程中删除任意元素,可以暂且把它理解为保存observer对象的集合。

如果ObserverWrapper不为null,则observe这个方法会直接return。ObserverWrapper在什么情况不为null呢?

如果观察者observer已经在mObservers这个列表中,并且observer已经有另一个所有者owner,就会不为null。

这是为了防止同一个observer被多个LifecycleOwner绑定,即一个LiveData中的同一个observer只能跟一个LifecycleOwner绑定。否则会抛出“Cannot add the same observer with different lifecycles”的异常。

在observe方法的最后,调用了owner.getLifecycle().addObserver(wrapper)方法,使得wrapper和owner的生命周期进行了绑定。也就是说,observer此时就可以监听到owner对应的界面的生命周期的变化了。

②setValue方法

setValue方法是用来更新LiveData数据的,当数据更新的时候就会通知观察者。

LiveData.java:

@MainThread

protected void setValue(T value) {

    assertMainThread("setValue"); //要求在主线程调用,若在其他线程使用postValue

    mVersion++; 辅助做数据是否有更新的判断

    mData = value;

    dispatchingValue(null);

}

首先调用assertMainThread检查是否在主线程,接着将value赋值给mData变量,同时mVersion变量加1,mVersion就是数据的版本,它用来标记数据是否变化。最后调用dispatchingValue()方法并传入null,将数据分发给各个观察者。

进入该方法:

LiveData.java:

void dispatchingValue(ObserverWrapper initiator) {

    //如果当前正在做数据分发,则不做重复处理

    if (mDispatchingValue) {

        mDispatchInvalidated = true;

        return;

    }

    mDispatchingValue = true; //标记事件分发状态为true

    do {

        mDispatchInvalidated = false;

        if (initiator != null) {

            //observe方法被调用时,从这里触发分发

            considerNotify(initiator);

            initiator = null;

        } else {

            //setValue触发遍历观察该LiveData的所有观察者,分发setValue事件

            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {

                considerNotify( iterator.next().getValue());

                if (mDispatchInvalidated) {

                    break;

                }

            }

        }

    } while (mDispatchInvalidated);

    mDispatchingValue = false;

}

可以看到,dispatchingValue方法参数传null和不传null的区别就是如果传null将会通知所有的观察者,反之仅仅通知传入的观察者。

在dispatchingValue方法中有个while循环,在while循环里面,由于参数initiator为null,所有走else分支通过遍历mObservers通知所有的观察者,将所有的ObserverWrapper拿到(实际上就是前面的LifecycleBoundObserver),通知观察者调用considerNotify方法,这个方法就是通知的具体实现了。

看看considerNotify方法,该方法针对每个观察者做处理,判断是否需要分发和完成分发事件:

LiveData.java:

private void considerNotify(ObserverWrapper observer) {

    //如果观察者不活跃就放弃分发,观察者活跃的逻辑是处于STARTED和RESUMED之间

    if (!observer.mActive) {

        return;

    }

    if (!observer.shouldBeActive()) {

        observer.activeStateChanged(false);

        return;

    }

    //如果该观察者已经接受过数据了,也不再进行分发

    if (observer.mLastVersion >= mVersion) {

        return;

    }

    observer.mLastVersion = mVersion; //对观察者进行分发版本记录

    observer.mObserver.onChanged((T) mData); //调用观察者的onChanged事件完成分发

}

首先做了一个判断,外部传入的observer对象是否处于活跃状态,不处于活跃状态则直接return这个方法。从这里就可看出,即使数据更新了,但如果界面不处于活跃状态,也不会对数据更新做响应。

然后是mVersion这个变量以及mLastVersion变量,判断observer.mLastVersion变量如果小于mVersion,会把mVersion的值赋值给observer.mLastVersion变量,这种情况就说明数据是新的数据,就可以执行observer的onChanged方法把mData回调出去了。

③postValue方法

LiveData.java:

protected void postValue(T value) {

    boolean postTask;

    synchronized (mDataLock) {

        postTask = mPendingData == NOT_SET;

        mPendingData = value;

    }

    if (!postTask) {

        return;

    }

    ArchTaskExecutor.getInstance().postToMai nThread(mPostValueRunnable);

}

postValue方法中,外部传进来的value会赋值给mPendingData,方法的最后会调用postToMainThread方法,postToMainThread,看这个方法的命名应该也能看出来一些东西,就是将这个runnable发布到主线程处理,进入postToMainThread详细看看:

ArchTaskExecutor.java:

@Override

public void postToMainThread(Runnable runnable) {

    mDelegate.postToMainThread(runnable);

}

它通过调用mDelegate的postToMainThread方法,mDelegate实际上就是DefaultTaskExecutor这个类,那么再进入这个类的postToMainThread看看:

DefaultTaskExecutor.java:

@Override

public void postToMainThread(Runnable runnable) {

    if (mMainHandler == null) {

        synchronized (mLock) {

            if (mMainHandler == null) {

                mMainHandler = new Handler(Looper.getMainLooper());

            }

        }

    }

    mMainHandler.post(runnable);

}

在这个方法中,先判断mMainHandler对象是否为null,如果为null,就创建一个,注意,它是通过Looper.getMainLooper()参数来创建的,说明这个这个mMainHandler就是主线程的handler对象。

最后调用mMainHandler.post(runnable)方法,将runnable对象交给主线程的handler来处理。

那么runnable执行了什么东西呢?

回到postValue方法,发现runnable就是mPostValueRunnable,看mPostValueRunnable是怎么定义的:

LiveData.java:

private final Runnable mPostValueRunnable = new Runnable() {

    @Override

    public void run() {

        Object newValue;

        synchronized (mDataLock) {

            newValue = mPendingData;

            mPendingData = NOT_SET;

        }

        setValue((T) newValue);

    }

};

看到它的run方法,先定义了个newValue变量,然后加锁对newValue赋值为mPendingData变量,mPendingData是在postValue方法内第三行赋值的,前面已经看到过了,然后将mPendingData置为NOT_SET。最后调用setValue方法。

所以,postValue方法实际上就是将线程切到主线程去处理setValue方法。

4.LiveData对生命周期的响应

举个例子:有一个界面对一个LiveData对象进行了监听,当界面处于非活跃状态时,如果更新了LiveData的数据,那么界面并不会马上响应LiveData的变化(因为界面处于非活跃状态嘛)。当界面重新回到活跃状态时,刚刚更新的LiveData数据会马上进行响应。

那为什么界面重新回到活跃状态时(比如activity从onPause状态回到onResume状态),刚刚更新的LiveData数据会马上进行响应呢?

这是因为在LiveData的observe方法中,通过owner.getLifecycle().addObserver(wrapper)方法,会使得wrapper和owner的生命周期进行了绑定,也就是说owner生命周期变化,wrapper就能在onStateChanged这个方法中监听到。

假设owner是个Activity,那么当Activity处于非活跃状态时,对LiveData进行了setValue操作,则在considerNotify方法的开头会直接被return,也就走不到observer.mObserver.onChanged方法了,也就不能接收到LiveData的数据变化。

当Activity回到活跃状态,wrapper(即LifecycleBoundObserver)的onStateChanged方法就会被回调:

LiveData.LifecycleBoundObserver类:

@Override

public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {

    if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {

        removeObserver(mObserver);

        return;

    }

    activeStateChanged(shouldBeActive());

}

如果Activity处于活跃状态时,就会执行到onStateChanged中的activeStateChanged方法:

void activeStateChanged(boolean newActive){

    if (newActive == mActive) {

        return;

    }

    mActive = newActive;

    boolean wasInactive = LiveData.this.mActiveCount == 0;

    LiveData.this.mActiveCount += mActive ? 1 : -1;

    if (wasInactive && mActive) {

        onActive();

    }

    if (LiveData.this.mActiveCount == 0 && !mActive) {

        onInactive();

    }

    //如果处理活跃状态,就执行dispatchingValue(this)方法

    if (mActive) {

        dispatchingValue(this);

    }

}

首先可以看到,mActive在这里被赋值。然后看到最后三行,判断如果处于活跃状态,就执行dispatchingValue(this)方法,那么这时又回到了setValue的那个流程中,也就是说,Activity生命周期变成活跃状态后,又能触发LiveData的值的更新了。

5.LiveData的自动解绑

LiveData能大大降低内存泄漏出现的可能,原因就是因为它能够自动跟它绑定的组件进行解绑,那么这是怎么做到的呢?还是看到LifecycleBoundObserver这个内部类:

LiveData.java:

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {

    final LifecycleOwner mOwner;

    LifecycleBoundObserver(LifecycleOwner owner, Observer<? super T> observer) {

        super(observer);

        mOwner = owner;

    }

    @Override

    public void onStateChanged( LifecycleOwner source, Lifecycle.Event event){

        if (mOwner.getLifecycle().getCurrentSta te() == DESTROYED) {

            removeObserver(mObserver);

            return;

        }

        activeStateChanged(shouldBeActive());

    }

    @Override

    boolean isAttachedTo(LifecycleOwner owner) {

        return mOwner == owner;

    }

    @Override

    void detachObserver() {

        mOwner.getLifecycle().removeObserver( this);

    }

}

onStateChanged方法中,如果监听到owner生命处于DESTROYED时,会执行LiveData类中的removeObserver方法,它的实现如下:

@MainThread

public void removeObserver(final Observer<? super T> observer) {

    assertMainThread("removeObserver");

    ObserverWrapper removed = mObservers.remove(observer);

    if (removed == null) {

        return;

    }

    //执行LifecycleBoundObserver的detachObserver方法

    removed.detachObserver();

    //执行ObserverWrapper的activeStateChanged方法

    removed.activeStateChanged(false);

}

removeObserver方法的主要流程分三个步骤:

①首先从mObservers集合中移除当前的observer对象。

②执行removed.detachObserver(),这行代码实际上会执行LifecycleBoundObserver的detachObserver方法,这里是真正将mOwner与mObserver解绑的地方。

③执行removed.activeStateChanged(false),这行代码实际上会执行ObserverWrapper的activeStateChanged方法,并把false传入。

6.LiveData的mData

在setValue方法中,value会赋值给LiveData的mData这个成员变量,看看这个mData。

private volatile Object mData = NOT_SET;

可以看到,它是被volatile修饰的,在java并发中,volatile是用来保证变量的可见性的,防止并发的情况下,多个线程对同一个变量进行修改,出现数据数据不一致的问题。

由此可以看出,LiveData中的mData是线程安全的。

LiveData中数据的更改:

①Transformations.map()

如果想要在LiveData对象分发给观察者之前对其中存储的值进行更改,可以使用Transformations.map()。

public class MainActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

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

        mutableLiveData.observe(this, new Observer<String>() {

            @Override

            public void onChanged(final String s) {

                Log.d(TAG, "onChanged1:"+s);

            }

        });

        LiveData transformedLiveData =Transformations.map(mutableLiveData, new Function<String, Object>() {

            @Override

            public Object apply(String name) {

               return name + "+Android开发者";

            }

        });

        transformedLiveData.observe(this, new Observer() {

            @Override

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged2:"+o.toString());

            }

        });

        mutableLiveData.postValue("Android最牛逼的语言");

    }

}

通过Transformations.map(),在mutableLiveData的基础上又加上了字符串。

打印结果:

D/MainActivity: onChanged1:Android最牛逼的语言

D/MainActivity: onChanged2:Android最牛逼的语言+Android开发者

②Transformations.switchMap()

如果想要手动控制监听其中一个的数据变化,并能根据需要随时切换监听,这时可以使用Transformations.switchMap(),它和Transformations.map()使用方式类似,只不过switchMap()必须返回一个LiveData对象。

public class MainActivity extends AppCompatActivity {

    MutableLiveData<String> mutableLiveData1;

    MutableLiveData<String> mutableLiveData2;

    MutableLiveData<Boolean> liveDataSwitch;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mutableLiveData1 = new MutableLiveData<>();

        mutableLiveData2 = new MutableLiveData<>();

        liveDataSwitch = new MutableLiveData<Boolean>();

        LiveData transformedLiveData= Transformations.switchMap(liveDataSwitch, new Function<Boolean, LiveData<String>>() {

            @Override

            public LiveData<String> apply(Boolean input) {

                if (input) {

                    return mutableLiveData1;

                } else {

                    return mutableLiveData2;

               }

            }

        });

        transformedLiveData.observe(this, new Observer<String>() {

            @Override

            public void onChanged(final String s) {

                Log.d(TAG, "onChanged:" + s);

            }

        });

        liveDataSwitch.postValue(false);//2

        mutableLiveData1.postValue("Android开发者");

        mutableLiveData2.postValue("Android语言无敌");

    }

}

新建一个MutableLiveData<Boolean>()来控制切换并赋值给liveDataSwitch,当liveDataSwitch的值为true时返回mutableLiveData1,否则返回mutableLiveData2,达到了切换监听的目的。

如果liveDataSwitch.postValue(false),打印Android语言无敌;

如果liveDataSwitch.postValue(true),打印Android开发者;

③合并多个LiveData数据源

MediatorLiveData继承自mutableLiveData,它可以将多个LiveData数据源集合起来,可以达到一个组件监听多个LiveData数据变化的目的。

public class MainActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

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

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

        MediatorLiveData liveDataMerger = new MediatorLiveData<String>();

        liveDataMerger.addSource( mutableLiveData1, new Observer() {

            @Override

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged1:"+o.toString());

            }

        });

        liveDataMerger.addSource( mutableLiveData2, new Observer() {

            @Override

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged2:"+o.toString());

            }

        });

        liveDataMerger.observe(this, new Observer() {

            @Override

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged:"+o.toString());

            }

        });

        mutableLiveData1.postValue("Android进阶之光");

    }

}

通过MediatorLiveData的addSource将两个MutableLiveData合并到一起,这样当任何一个MutableLiveData数据发生变化时,MediatorLiveData都可以感知到。

7.LiveData跨组件通信

LiveData可以及时通知组件数据发生变化,还能自动解除绑定,但是LiveData的用途还不止这些。

不知道大家有没有用过EventBus,这个东西可以代替Android传统的Intent、Handler、Broadcast或接口回调,在Fragment、Activity、Service线程之间传递数据,执行方法。EventBus最大的特点就是简洁、解耦,它的框架思想就是消息的发布和订阅,它通过解耦发布者和订阅者简化Android事件传递。

其实,通过LiveData也能实现类似于EventBus的框架,并且使用后不需要手动解除绑定,更加方便和优雅。那么,怎么实现呢?

首先能想到,可以将一个LiveData设置为static的,这样不同的Activity或Fragment就都能访问这个变量,那么监听这个变量值变化也不成问题。这个方法确实能实现LiveData跨Activity/Fragment通信。

举个例子:假设有两个界面,分别是Activity1和Activity2,Activity1通过LiveData发送数据,然后在Activity2中接受数据。那么可以新建一个MyStaticLiveData类,用来保存公共的LiveData:

class MyStaticLiveData {

    companion object {

        val liveData = MutableLiveData<String>()

    }

}

然后Activity1中发送数据,并且有个button用来跳转到Activity2:

class Activity1 : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView( R.layout.activity_live_data)

        MyStaticLiveData.liveData.value = "data from LiveDataActivity."

    }

    fun toLiveData2Activity(view: View) {

        startActivity(Intent(this, Activity2::class.java))

    }

}

Activity2中将公共的LiveData和Activity2进行绑定监听,这样就能接受到Activity1发送过来的数据了,并且也不需要在onDestroy中解绑LiveData,因为它会自动解绑。

class Activity2 : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView( R.layout.activity_live_data2)

        MyStaticLiveData.liveData.observe(this, {

            tv2.text = it

        })

    }

}

这样看来,确实实现了EventBus粘性事件的功能,并且用法更加简洁。

但是它的缺点也是很明显的,它不够优雅,而且,每当想搞个新的LiveData的时候,都需要在MyStaticLiveData中手动创建,另外,没有一个东西统一来管理这些LiveData,并且,每个LiveData都是static的,所以,开发中不建议这么写,这里只做演示用,不建议用到实际开发场景中 。

另外,在开发过程中,粘性事件大部分情况下都是不需要用到的。而更多的场景是,我们希望监听者能在注册监听后才开始接收发送过来的消息,在注册前发送的消息一律不接收。

粘性事件是指当LiveData在观察者开始观察之前就发生改变,当观察者开始观察后。这个改变仍然会通知到观察者,这就是粘性事件。

8.数据倒灌

刚刚演示的例子,业内有另一种更常见的叫法,数据倒灌。一句话概括就是,先给LiveData设置了value,然后监听者才开始对LiveData进行监听,这时LiveData的value会立马回调给监听者。

数据倒灌,即在setValue后,observe对此次set的value值会进行多次消费。比如进行第二次observe的时候获取到的数据是第一次的旧数据。这样会带来不可预期的后果。

产生数据倒灌的原因还得从源码的角度进行分析,先看setValue方法:

LiveData.java:

static final int START_VERSION = -1;

private int mVersion = START_VERSION;

@MainThread

protected void setValue(T value) {

    assertMainThread("setValue");

    mVersion++;

    mData = value;

    dispatchingValue(null);

}

注意这里的mVersion,在setValue中进行了自增操作。另外,mVersion的值默认为-1。所以第一次调用setValue时,LiveData中的mVersion的值等于0。

当调用LiveData进行observe时,observe最终会走到considerNotify方法:

LiveData.java:

private void considerNotify(ObserverWrapper observer) {

    if (observer.mLastVersion >= mVersion) {

        return;

    }

    observer.mLastVersion = mVersion;

    observer.mObserver.onChanged((T) mData);

}

private abstract class ObserverWrapper {

    ......

    int mLastVersion = START_VERSION;

    ......

}

可以看到,当observer的mLastVersion小于mVersion时就会把之前的数据回调给监听者。另外,observer中的mLastVersion默认值也为-1,所以在considerNotify中,很明显,mLastVersion小于mVersion(-1 < 0),所以会执行onChanged方法把数据回调给监听者。

注:被观察者数据发生变化,如何确定这次变化需要通知到观察者?在livedata源码中,有俩版本号用作该控制。第一个版本号是livedata的版本号,属于被观察者的,第二个版本号是该livedata的每一个观察者都有一个lastVersion,每次分发成功后会同步这两个版本号的值,将每个观察者的lastVersion更新位livedata的版本号。

数据倒灌举例:比如旋转屏幕时会导致Activity重建,Activity异常销毁然后重建,ViewModel会保存销毁之前的数据,然后在Activity重建完成后进行数据恢复,所以LiveData成员变量中的mVersion会恢复到重建之前的值。但是Activity重建后会调用LiveData的observe()方法,方法内部会重新new一个实例,会将mLastVersion恢复到初始值。由于LiveData本身的特性,Activity的生命周期由非活跃变成活跃时,LiveData会触发事件分发,导致屏幕旋转后出现数据倒灌。

注:在Activity屏幕旋转的状态下ViewModel并不会被销毁,这样ViewModel只要持有Activity的所有数据,都可以进行状态恢复。

9.数据倒灌解决方案

①反射修改mVersion。

根据之前的分析可以知道,LiveData判断这个事件是否分发出去的关键在considerNotify方法中。

private void considerNotify(ObserverWrapper observer) {

    if (!observer.mActive) {

        return;

    }

    if (!observer.shouldBeActive()) {

        observer.activeStateChanged(false);

        return;

    }

    if (observer.mLastVersion >= mVersion) {

        return;

    }

    observer.mLastVersion = mVersion;

    //noinspection unchecked

    observer.mObserver.onChanged((T) mData);

}

每次setValue或postValue时,mVersion会+1,只要mLastVersion>=mVersion即证明之前有过setValue或postValue。现在我们想使在observer调用前的setValue方法不被分发出去,只需要在调用observer之前的某个节点处改变使其mLastVersion = mVersion即可。

通过源码发现可以通过反射在observer中找到mObservers对象和当前mVersion,然后便可以在这里将mVersion赋值给mLastVersion。

private void hook(@NonNull Observer observer) throws Exception {

    //get wrapper's version

    Class classLiveData = LiveData.class;

    Field fieldObservers = classLiveData.getDeclaredField("mObservers");

    fieldObservers.setAccessible(true);

    Object objectObservers = fieldObservers.get(this);

    Class> classObservers = objectObservers.getClass();

    Method methodGet = classObservers.getDeclaredMethod("get", Object.class);

    methodGet.setAccessible(true);

    Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);

    Object objectWrapper = null;

    if (objectWrapperEntry instanceof Map.Entry) {

        objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();

    }

    if (objectWrapper == null) {

        throw new NullPointerException( "Wrapper can not be bull!");

    }

    Class> classObserverWrapper = objectWrapper.getClass().getSuperclass();

    Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");

    fieldLastVersion.setAccessible(true);

    //get livedata's version

    Field fieldVersion = classLiveData.getDeclaredField("mVersion");

    fieldVersion.setAccessible(true);

    Object objectVersion = fieldVersion.get(this);

    //set wrapper's version

    fieldLastVersion.set(objectWrapper, objectVersion);

}

然后重写LiveData,将这个hook方法放在observe方法中。

这样一来,使用该自定义的LiveData时就会发现,先setValue后observe的做法已经行不通了,这就是所谓的非粘性。

②利用SingleLiveEvent

SingleLiveEvent,顾名思义是一个只会发送一次更新的LiveData。它可以使observe#LiveData时只相应一次onChanged操作。

其代码实现如下:

class SingleLiveEvent<T> : MutableLiveData<T>() { 

    private val mPending = AtomicBoolean(false) 

    @MainThread

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {

        if (hasActiveObservers()) {

            Log.w("SingleLiveEvent", "Multiple observers registered but only one will be notified of changes.")

        }

        super.observe(owner, { t ->

            if (mPending.compareAndSet(true, false)) {

                observer.onChanged(t)

            }

        })

    } 

    @MainThread

    override fun setValue(t: T?) {

        mPending.set(true)

        super.setValue(t)

    }

    @MainThread

    fun call() {

        value = null

    }

}

SingleLiveEvent的思路是,在每次onChanged触发时,会通过一个布尔值mPending来判断上一次的setValue事件有没有被消费,如果被消费过了,则不再将消费传递下去。

第一步:SingleLiveEvent利用AtomicBoolean(默认为false)进行赋值,当LiveData进行setValue时改变AtomicBoolean的值(set(true))。

第二步:使用AtomicBoolean.compareAndSet( true,false)方法,先进行判断(此时的AtomicBoolean的值为true)与 compareAndSet设置的except值(第一个参数)比较,因为相等所以将第二个参数设置为AtomicBoolean值设为false函数并返回true),这里其实就是利用了AtomicBoolean原子性,执行了一个比较和重新赋值的过程。

第三步:当再次进入该页面虽然LiveData值并没有改变,仍然触发了observer方法,由于 AtomicBoolean已经为false,但是except值为true,与if 进行判断所以并不会继续触发onChanged(T)方法,即只有在setValue时响应一次onChanged(T)方法。

实际上,SingleLiveEvent并没有解决‘粘性’的问题。它所适用的场景是一次setValue后,多次observe,却只想消费一个observe。但是,SingleLiveEvent 的问题在于它仅限于一个观察者。如果无意中添加了多个,则只会调用一个,并且不能保证哪一个。

③UnPeekLiveData

这个是KunMinX大神所开源的一个解决此类问题的方法。

public class ProtectedUnPeekLiveData extends LiveData {

    protected boolean isAllowNullValue;

    private final HashMap observers = new HashMap();

    public void observeInActivity(@NonNull AppCompatActivity activity, @NonNull Observer super T> observer) {

        LifecycleOwner owner = activity;

        Integer storeId = System.identityHashCode(observer);//源码这里是activity.getViewModelStore(),是为了保证同一个ViewModel环境下"唯一可信源"

        observe(storeId, owner, observer);

    }

    private void observe(@NonNull Integer storeId,@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {

        if (observers.get(storeId) == null) {

            observers.put(storeId, true);

        }

        super.observe(owner, t -> {

            if (!observers.get(storeId)) {

                observers.put(storeId, true);

                if (t != null || isAllowNullValue) {

                    observer.onChanged(t);

                }

            }

        });

    }

    @Override

    protected void setValue(T value) {

        if (value != null || isAllowNullValue) {

            for (Map.Entry entry : observers.entrySet()) {

                entry.setValue(false);

            }

            super.setValue(value);

        }

    }

    protected void clear() {

        super.setValue(null);

    }

}

其思路也很清晰,为每个传入的observer对象携带一个布尔类型的值,作为其是否能进入observe方法的开关。每当有一个新的observer存进来的时候,开关默认关闭。

每次setValue后,打开所有Observer的开关,允许所有observe执行。

同时方法进去后,关闭当前执行的observer开关,即不能对其第二次执行了,除非你重新setValue。

通过这种机制,使得 不用反射技术实现LiveData的非粘性态 成为了可能。

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/130068181