Jetpck之LiveData解析

1. LiveData简介与使用

1.1 简介

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

1.2 简单使用

  • 通常LiveData都是作为ViewModel层存在于ViewModel中
kotlin 复制代码
class NameViewModel : ViewModel() {

    private val _currentName: MutableLiveData<String> = MutableLiveData()

    // 只暴露不可变的LiveData的目的是防止外界变更,使得ViewModel才是唯一可信来源
    val currentName: LiveData<String> = _currentName

    fun fetchUserName() {
        viewModelScope.launch {
            delay(2000)
            _currentName.value = "BTPJ"
        }
    }
}
kotlin 复制代码
class NameActivity : AppCompatActivity() {
    /** 使用activity-ktx依赖中的代理直接赋值 */
    private val model: NameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_name)

        // LiveData监听
        model.currentName.observe(this) {
            findViewById<TextView>(R.id.tv_name).text = it
        }
    }
}
  • 更多关于LiveData的扩展转换以及合并可直接查看官方教程,这里主要分析LiveData的源码

2. 源码分析

2.1 添加观察者

  • LiveData主要是用到了观察者模式,其中添加观察者是通过observe()
java 复制代码
// LiveData.java
/**
 * 添加观察者. 事件在主线程分发. 如果LiveData已经有数据,将直接分发给observer。 
 * 观察者只在LifecycleOwner活跃时才接收事件,如果变为DESTROYED状态,observer自动移除。 
 * 当数据在非活跃时更新,observer不会接收到。变为活跃时 将自动接收前面最新的数据。 
 * 
 * @param owner 观察绑定的LifecycleOwner
 * @param  Observer 要添加的观察者
 */
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // 限制主线程调用
    assertMainThread("observe");
    
    // 当绑定的LifecycleOwner为DESTROYED状态时直接忽略
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        return;
    }
    
    // 将owner和observer一起包装成LifecycleBoundObserver对象
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 将observer作为key,wrapper作为value存入Map中,putIfAbsent表示当前是否存在该  
    // observer,存在就直接返回已存储的ObserverWrapper值,不存在就直接添加
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 同一个观察者与不同的LifecycleOwner绑定直接报错
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    // 已存在就不再重复绑定
    if (existing != null) {
        return;
    }
    // 不存在就让wrapper观察owner的生命周期,即绑定生命周期
    owner.getLifecycle().addObserver(wrapper);
}
  • 主要做了两件事
    *
    1. 将观察者缓存进Map
      1. 将观察者的观察周期与LifecycleOwner的生命周期绑定

2.2 事件分发回调

2.2.1 setValue/postValue

  • 先从LiveData设置数据开始着手,postValue是任意线程(主要针对子线程)调用,而setValue则只能是主线程调用
java 复制代码
/** 从其他线程发送数据到主线程 */
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        // 数据缓存至mPendingData
        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) {
            // 取出postValue()缓存的value
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 最终还是调用了setValue(),只不过帮你切到了主线程
        setValue((T) newValue);
    }
};
  • 由此可见postValue()本质上还是调用了setValue(),只不过帮你从子线程切到了主线程,简单看下是怎么切的
java 复制代码
// DefaultTaskExecutor.java
@Override
public void postToMainThread(Runnable runnable) {
    if (mMainHandler == null) {
        synchronized (mLock) {
            if (mMainHandler == null) {
                mMainHandler = createAsync(Looper.getMainLooper());
            }
        }
    }
    mMainHandler.post(runnable);
}
  • 没有其他办法,果然还是通过Handler
  • 接着看下setValue()
java 复制代码
@MainThread
protected void setValue(T value) {
    // 限制只能在主线程调用
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    // 关键事件分发
    dispatchingValue(null);
}
  • 接着看分发事件的方法dispatchingValue(null),注意传递的参数为null
java 复制代码
/** 分发事件 */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
           // setValue传值为null走的这里
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
  • 可以看到最终会遍历所有的观察者,并逐个调用considerNotify(observer)
java 复制代码
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
       // 若当前observer对应owner非活跃,就会调用activeStateChanged方法,
       // 并传入false,其内部会再次判断
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 这里就是关键的onChanged(mData)回调,会在活跃状态执行
    observer.mObserver.onChanged((T) mData);
}

/** 判断是否是活跃状态(RESUMED状态) */
@Override
boolean shouldBeActive() {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    
    mActive = newActive;
    changeActiveCounter(mActive ? 1 : -1);
    if (mActive) { 
        dispatchingValue(this);
    }
}
  • 整个过程就是一个典型的观察者模式,只是其中融入了对LifecycleOwner是否活跃的判断,只有在活跃时才回调。

2.2.3 粘性事件

  • 粘性事件即发送消息事件早于注册事件,依然能够接收到消息通知
  • LiveData默认就支持粘性事件(而且没有关闭开关),简单分析下支持的原理,主要切入点还是所谓后注册的注册事件observe()
java 复制代码
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
   ...
   LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
   ...
   owner.getLifecycle().addObserver(wrapper);
}
  • 核心在于owner.getLifecycle().addObserver(wrapper),addObserver()是Lifecycle库用来添加一个LifecycleObserver,当LifecycleOwner改变状态时,它会被通知,通知回调是onStateChanged(...),看下这个wrapper(即LifecycleBoundObserver对象)的onStateChanged()方法
java 复制代码
// LiveData.java
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) {
           // 观察者是DESTROYED状态时移除观察
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            // 关键代码
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
    ...
}
  • 与上面一样最终还是会执行activeStateChanged(),传参为shouldBeActive()即是否处于活跃状态
java 复制代码
void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    
    mActive = newActive;
    changeActiveCounter(mActive ? 1 : -1);
    if (mActive) { 
        // 当判断为活跃状态时即会执行下面分发事件代码
        dispatchingValue(this);
    }
}
  • 最终会执行dispatchingValue(this),流程跟上面一样,只是传参不再是null了,简单再看下里面的逻辑
java 复制代码
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    ...
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            // 有值会执行这里
            considerNotify(initiator);
            initiator = null;
        } else {
           ...
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
  • 可见最终还是会执行considerNotify(initiator),只不过不再是循环执行所有观察者,而是只针对那一个观察者

3、LiveData存在的问题

3.1 数据倒灌

3.1.1 问题描述

  • 所谓数据倒灌,其实就是新添加的监听依然会收到老数据的发送,其实很好理解,就是粘性特性带来的弊端,粘性特性有时候很有用,但有时候又会带来灾难,但LiveData官方并没有提供像EventBus那样取消粘性的Api,所以这个灾难就产生了一个新词即数据倒灌。
  • 举一个简单又常见的例子:ViewModel数据请求时出错了,我们Activity收到错误进行Toast提示,这时当Activity配置改变(如旋转屏幕),这个错误会又提示一遍
kotlin 复制代码
class MainViewModel : BaseViewModel() {

    private val _errorLiveData = MutableLiveData<String>()
    val errorLiveData: LiveData<String> = _errorLiveData

    fun loadData() {
        viewModelScope.launch {
            LogUtil.d("LTP","开始请求")
            delay(1000)
            _errorLiveData.value = "模拟请求出错"
        }
    }
}
kotlin 复制代码
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mViewModel.apply {
            errorLiveData.observe(this@MainActivity) {
                LogUtil.d("LTP", it)
                ToastUtil.showShort(this@MainActivity, it)
            }

            // 避免说是受到多次请求的影响,加个判断只请求一次
            if (errorLiveData.value == null) { 
                loadData()
            }
        }
    }
}

运行结果:
开始请求
模拟请求出错

-----屏幕翻转后----
模拟请求出错
  • 原因很简单,屏幕翻转时,会重新执行Activity的onCreate(),observe()会再次执行,由于粘性事件的原因,那个错误值依然会被接收到

3.1.2 解决方式

  • 所谓处理数据倒灌最典型的思路一种是直接让LiveData取消粘性特性,一种是在自定义LiveData并在onChange()回调中做判定
  1. 使用第三方库UnPeek-LiveData
  2. 使用事件包装器
  3. SingleLiveEvent
  4. 反射干涉Version

3.2 postValue导致的数据丢失

3.2.1 问题描述和产生原因

  • 问题描述: 当使用postValue()发送数据时,生产速度(postValue)超过消费速度(onChange)时,会存在中间数据丢失的问题
  • 产生原因: 回顾下postValue
java 复制代码
/** 从其他线程发送数据到主线程 */
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        // 数据缓存至mPendingData
        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) {
            // 取出postValue()缓存的value
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 最终还是调用了setValue(),只不过帮你切到了主线程
        setValue((T) newValue);
    }
};
  • 当发送的旧消息还未及时消费,新消息中的value就会赋值到缓存的mPendingData,再进行消费时取出的newValue就是新的value,从而导致旧的value丢失

3.2.2 解决方式

  • 可以使用Kotlin的Flow(支持缓存背压),或者Rxjava的flowable

3.3 数据抖动

  • 原因:由于LiveData并未对发送的旧值与新值做对比,所以当同一个数据被多次发送时都会被回调执行
  • 解决方案 :自定义LiveData在onChange中判断处理或者将LiveData换成stateFlow

4. 参考文章

相关推荐
500了4 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵4 小时前
Android Debug Bridge(ADB)完全指南
android·adb
小雨cc5566ru9 小时前
uniapp+Android面向网络学习的时间管理工具软件 微信小程序
android·微信小程序·uni-app
bianshaopeng10 小时前
android 原生加载pdf
android·pdf
hhzz11 小时前
Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)
android·linux·jar
火红的小辣椒12 小时前
XSS基础
android·web安全
勿问东西13 小时前
【Android】设备操作
android
五味香13 小时前
C++学习,信号处理
android·c语言·开发语言·c++·学习·算法·信号处理
图王大胜15 小时前
Android Framework AMS(01)AMS启动及相关初始化1-4
android·framework·ams·systemserver
工程师老罗17 小时前
Android Button “No speakable text present” 问题解决
android