理解Jetpack--LiveData

256 阅读2分钟

什么是LiveData


LiveData是一个可以感知(Activity/Fragment)生命周期的数据容器,它具备如下几个特性:

  • 它会在宿主生命周期进入DESTORY状态时自动移除掉观察者。
  • 当宿主的生命周期处于活跃状态STARTEDRESUMED状态才会触发数据的分发。
  • 它是线程安全的,它内部使用的Synchronized处理线程安全问题。

LiveData使用


LiveData使用时,我们可以在主线程中通过setValue更新数据,在子线程中可以通过postValue更新数据。代码如下:

class MainViewModel : ViewModel() {
    private val _userLiveData = MutableLiveData<String>()

    val userLiveData: LiveData<String>
        get() = _userLiveData

    fun setUser(name: String) {
        _userLiveData.value = name
    }

    fun postUser(name: String) {
        _userLiveData.postValue(name)
    }

}
class MainActivity : AppCompatActivity() {

    private val viewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel.userLiveData.observe(this) {
            Log.d("MainActivity", "userName:$it")
        }

        //主线程中更新LiveData数据
        viewModel.setUser("小虎")

        //子线程中更新LiveData数据
        Thread {
            viewModel.postUser("小米")
        }.start()
    }
}

LiveData源码分析

生命周期

我们说LiveData是可以感知生命周期的数据容器,我们来看一下,为什么可以感知生命周期。我们来分析observe方法,源码如下。

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}

我们看到我们注册的Observer被放进了LifecycleBoundObserver中,LifecycleBoundObserver被放进了mObservers集合中,如果集合中存在不会重复添加,最后LifecycleBoundObserver被注册给lifecycle。

LifecycleBoundObserver

我们再看一下LifecycleBoundObserver中做了什么。源码如下:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
}

我们看到LifecycleBoundObserver继承了ObserverWrapper,我们自己写的Observer会被包装到ObserverWrapper中。同时LifecycleBoundObserver实现了LifecycleEventObserver,这样该类就具备了观察Activity/Fragment生命周期的能力,在observer方法中调用了mOwner.getLifecycle().addObserver()注册该观察者,这样当我们的宿主生命周期发生变化时,就会调用onStateChanged方法。

onStateChanged

上面说到宿主的生命周期发生变化后,会调用onStateChanged方法,我们看一下该方法中有做了什么处理。

public void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {
    Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
    if (currentState == DESTROYED) {
        removeObserver(mObserver);
        return;
    }
    Lifecycle.State prevState = null;
    while (prevState != currentState) {
        prevState = currentState;
        activeStateChanged(shouldBeActive());
        currentState = mOwner.getLifecycle().getCurrentState();
    }
}

该方法会先判断当前状态是否为DESTROYED,如果是会把我们自己写的Observer移除。如果不是会调用activeStateChanged,同时修改当前的状态。

activeStateChanged

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive
    // owner
    mActive = newActive;
    changeActiveCounter(mActive ? 1 : -1);
    if (mActive) {
        dispatchingValue(this);
    }
}

在该方法中会修改我们自己注册的Observer的激活状态mActive,如果是激活状态会调用dispatchingValue分发数据。dispatchingValue方法这里暂时不详解,在后面setValue方法时再做详细分析。

setValue

我们使用setValue时会直接修改LiveData的值同时修改LiveData版本号mVersion,最后会去调用dispatchingValue分发数据。这里的数据分发不一定会真实的触发数据的分发,因为数据的分发需要几个条件,宿主的生命周期处于活跃状态、数据版本号发生改变才会去触发。

protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

dispatchingValue

上面我们说到宿主的生命周期发生变化和setValue方法都会调用该方法,我们来看一下该方法是如何分发数据的。源码如下:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

该方法会通过mDispatchingValue判断当前LiveData是否正在分发数据,如果正在分发数据修改mDispatchInvalidated为true,表示正在分发的数据为无效状态,会使正在分发的数据重新进入while循环,去使用最新的数据做分发。这种情况大部分在多线程或数据发送过快时出现。如果当前LiveData没有正在分发数据,会分两种情况分发数据1.有固定的监听者,只会把数据分发给该监听者。该情况可以查看生命周期状态变化时的分发数据。2没有固定的监听者,会遍历所有的监听者并分发数据。分发数据会调用considerNotify方法。

considerNotify

我们看一下该方法是如何具体分发数据的,源码如下:

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
    //
    // we still first check observer.active to keep it as the entrance for events. So even if
    // the observer moved to an active state, if we've not received that event, we better not
    // notify for a more predictable notification order.
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

我们可以看到当宿主处于活跃状态,会去比较observer中的数据版本号mLastVersion和当前LiveData的数据版本号mVersion,如果LiveData当前的版本大,会调用observer的onChanged方法分发数据。这里数据分发的条件之一是比较的数据版本,而不是数据本身,也就是说我们的数据相同,版本号不同也是会重新分发数据的。

postValue

上面我们分析了如何在主线程中调用setValue更新数据,下面我们看看如何在子线程中调用postValue更新数据的。

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

这里会先判断是否有正在处理分发的任务,然后把数据给一个临时变量mPendingData,如果有正在分发的任务该方法就结束了。没有正在分发的任务会调用ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable)在主线程中处理,这里使用的是Handler的post,这里就详细说了。在Runnable中还是调用的setValue方法去更新数据的。

LiveData常见问题

数据丢失

数据丢失分为多种情况。

一种是宿主在非活跃状态多次调用setValue和postValue会导致中的的数据无法接收,造成数据丢失。

另外的一种是postValue造成的数据丢失,postValue为什么会造成数据的丢失,源码如下:

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    //代码2
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
          //代码1
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};
  1. 在主线程执行到代码1之前的位置,再次调用postValue时,因为mPendingData不为NOT_SET,会在更新mPendingData完值后退出该方法,造成数据丢失。

  2. 在多线程的情况下,当一个线程执行到代码2的位置,另外的一个线程执行postValue方法,因为mPendingData不为NOT_SET,会在更新mPendingData完值后退出该方法,造成数据丢失。

粘包和数据倒灌

粘性事件

什么是粘性事件,就是我们先修改LiveData值,再注册Observer仍然能收到数据。如下代码在activity生命周期变成活跃状态时就会发生粘性事件。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val liveData=MutableLiveData<String>()
        liveData.value="冬至"
        liveData.observe(this) {
            Log.d("MainActivity", "收到数据:$it")
        }
    }
}

我们来分析为什么会发生粘性事件,有两个地方导致的。

一是因为在setValue时会修改LiveData的版本号mVersion++,而新注册的Observer的mLastVersion=-1,数据分发时对比的是LiveData的mVersion与Observer的版本mLastVersion,所以mVersion>mListVersion触发数据分发。

数据倒灌

Activity异常销毁然后重建,ViewModel会保存销毁之前的数据,然后在Activity重建完成后进行数据恢复,所以LiveData成员变量中的mVersion会恢复到重建之前的值。但是Activity重建后会调用LiveData的observe()方法,方法内部会重新new一个LifecycleBoundObserver实例,其内部的mLastVersion=-1由于粘性事件的特性,Activity的生命周期由非活跃变成活跃时,LiveData会触发事件分发出现数据倒灌。

解决粘性事件或数据倒灌

  1. 重写LiveData的observer方法,通过反射拿到LiveData的版本号mVersion,修改observer的mLastVersionmVersion。这样可以保证版本号的一致,把粘性事件变成非粘性事件,也可以解决数据倒灌的问题。
  2. 某位大神提供的SingleLiveEvent,可以解决数据倒灌问题,不能解决粘性问题。
  3. UnPeekLiveData,这个是KunMinX开源的一个解决此类问题的方法,其可以把粘性变成非粘性,同时可以防止数据倒灌。其思路是,为每个传入的observer对象携带一个布尔类型的值,作为其是否能进入observe方法的开关。每当有一个新的observer存进来的时候,开关默认关闭。每次setValue后,打开所有Observer的开关,允许所有observe执行。同时方法进去后,关闭当前执行的observer开关,即不能对其第二次执行了,除非你重新setValue。