JetPack 之 LiveData

Q:什么是LiveData?

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

Q:请谈谈LiveData的好处

1.确保界面符合数据状态
LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
2.不会发生内存泄露
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
3.不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
4.不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
5.数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
6.适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
7.共享资源
您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。

Q:LiveData为什么可以自动取消订阅,如何避免内存泄漏?

调用 observe 方法时,会调用 owner.getLifecycle().addObserver 以达到感知生命周期的目的。

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer 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);
    }

其中的观察者是owner和observer的包装对象LifecycleBoundObserver

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

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

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

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

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

当Lifecycles的State发生变化会回调onStateChanged方法,当State为DESTROYED时,则移除观察者Observer。里面调用的是LiveData的removeObserver方法。

public void removeObserver(@NonNull final Observer observer) {
        assertMainThread("removeObserver");
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        removed.detachObserver();
        removed.activeStateChanged(false);
    }

当页面销毁时,在mObservers中remove了observer,就这样完成了订阅的自动取消。

Q:LiveData传相同的值会不会执行onchanged回调?

给LiveData传值有两种方式setValue()postValue(),它们之间的区别在于前者只能在主线程使用,后者可以在任意线程中调用,传入的数据会暂存为mPendingData,最终会使用Handler切换回主线程中调用setValue(mPendingData)进行数据更新。

注意的是,postValue()被多次调用时,暂存数据mPendingData会被postValue()传入的数据覆盖,最终数据为最后一次的数据。而postValue()发起的主线程任务,在执行到之前,只会存在一个任务。

查看setValue方法:

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

注意mVersion的值,查看dispatchingValue方法:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        ....
        considerNotify(initiator);
        ....
    }

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);
    }

只要mVersion是大于等于之前的值,就会回调onChanged方法,也就是说,不管值是否相同,只看version的值。

Q:谈谈你对observeForever的认识?

// LiveData.java
@MainThread
public void observeForever(@NonNull Observer observer) {
    assertMainThread("observeForever");
    // 创建AlwaysActiveObserver
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        // 如果existing是LiveData.LifecycleBoundObserver类的实例,抛出异常
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    // 调用AlwaysActiveObserver的activeStateChanged方法,并且传入true
    wrapper.activeStateChanged(true);
}

再看下AlwaysActiveObserver的代码:

// LiveData.java
private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer observer) {
        super(observer);
    }

    // 重写了shouldBeActive方法,并且返回true,根据上面的代码分析可知,这个方法是用来判断是否为活跃状态,这里一直返回true,也就是说一直保持着活跃状态
    @Override
    boolean shouldBeActive() {
        return true;
    }
}

observeForever是用于将指定的观察者添加到观察列表中,类似于调用observer方法,但是给定的LifecycleOwner状态总是为活跃状态,这意味着观察者将永远接收所有的事件,所以如果要停止观察这个LiveData,就要手动调用removeObserver方法。

Q:PostValue收不到数据变更的通知的问题是否遇到过?或者说使用PostValue需要注意的问题
当连续调用 postValue 时,有可能只会收到最后一次数据更新通知。

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

mPendingData 被成功赋值 value 后,post 了一个 Runnable
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);
    }
};
  • postValue 将数据存入 mPendingData,mPostValueRunnable 在UI线程消费mPendingData。
  • 在 Runnable 中 mPendingData 值还没有被消费之前,即使连续 postValue , 也不会 post 新的 Runnable
  • mPendingData 的生产 (赋值) 和消费(赋 NOT_SET) 需要加锁
    简单的说是因为:

postValue 只是把传进来的数据先存到 mPendingData,然后往主线程抛一个 Runnable,在这个 Runnable 里面再调用 setValue 来把存起来的值真正设置上去,并回调观察者们。而如果在这个 Runnable 执行前多次 postValue,其实只是改变暂存的值 mPendingData,并不会再次抛另一个 Runnable。这就会出现后设置的值把前面的值覆盖掉的问题,会导致事件丢失。

Q:为什么PostValue要设计为只post一次Runnable?

即使 post 多次也没有意义,所以只 post 一次即可。

对于 setValue 来说,连续调用多次,数据会依次更新:
如下,订阅方一次收到 a b 的通知:

liveData.setValue("a");
liveData.setValue("b");

通过源码可知,dispatchingValue()中同步调用 Observer#onChanged(),依次通知订阅方:

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

但对于 postValue,如果当 value 变化时,我们立即post,而不进行阻塞

protected void postValue(T value) {
    mPendingData = value;
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    public void run() {
        setValue((T) mPendingData);
    }
};

由于线程切换的开销,连续调用 postValue,收到通知只能是b、b,无法收到a。

因此,post 多次已无意义,一次即可。

Q: 为什么要加读写锁?

是否 post 取决于对 mPendingData 的判断(是否为 NOT_SET)。因为要在多线程环境中访问 mPendingData ,不加读写锁无法保证其线程安全。

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

private final Runnable mPostValueRunnable = new Runnable() {
    public void run() {
        Object newValue = mPendingData;
        mPendingData = NOT_SET; // --3
        setValue((T) newValue);
    }
};

如上,如果在 1 和 2 之间,执行了 3,则 2 中设置的值将无法得到更新。

Q:如何解决或防止LiveData或者MutableLiveData多次回调的问题
利用SingleLiveEvent 使 observe#LiveData时只相应一次onChanged操作

public class SingleLiveEvent extends MutableLiveData {

    private static final String TAG = "SingleLiveEvent";

    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer observer) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}

1.SingleLiveEvent 利用 AtomicBoolean (默认为false)进行赋值,当LiveData 进行 setValue时改变 AtomicBoolean的值(set(true)

2 使用 AtomicBoolean.compareAndSet(true,false)方法,先进行判断(此时的AtomicBoolean的值为true)与 compareAndSet设置的except值(第一个参数)比较,因为相等所以将第二个参数设置为AtomicBoolean值设为false函数并返回 true

  1. 当再次进入该页面虽然 LiveData值并没有改变,仍然触发了 observer方法,由于 AtomicBoolean已经为 false ,但是 except值为 true,与if 进行判断所以 并不会继续触发 onChanged(T)方法

即只有在 setValue时相应一次onChanged(T)方法。

Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus

ViewModel的左膀右臂 数据驱动真的香

ViewModel之外的LiveData-使用Transformations和MediatorLiveData的反应模式

Android面试:说一下 LiveData 的 postValue ?与SetValue有什么区别?连续调用会有什么问题?为什么?

带有SnackBar、Navigation和其他事件的LiveData

你可能感兴趣的:(JetPack 之 LiveData)