LiveData的基本使用与原理解析

LiveData的基本使用

创建一个LiveData的类:

kotlin 复制代码
object MyLiveData { // 单例
    // 懒加载
    val info1: MutableLiveData<String> by lazy { MutableLiveData() }
}

在Activity中:

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView : TextView = findViewById(R.id.tv_textview)

        // 1 观察者 眼睛 环节
        MyLiveData.info1.observe(this, {
            textView.text = it // 更新UI
        })
        
        ```
        // 手动考虑释放工作
        MyLiveData.value1.observeForever({

        })
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 手动释放
        // MyLiveData.value1.removeObserver()
    }
}

LiveData存在数据黏性的问题,就是先设置值,后订阅也能收到值,这是因为一般的,我们只关心订阅之后的数据变化。但这种行为在某些情况下是有用的,比如配置更改(如屏幕旋转)后恢复UI状态,但在其他场景中可能导致不希望的重复数据更新。 如何解决这个问题呢?可以通过反射来处理:

js 复制代码
/**
 * 单例模式 去掉黏性事件(有关闭黏性的开关) KT的版本
 */
object OKLiveDataBusKT {

    // 存放订阅者
    private val bus : MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap() }

    // 暴露一个函数,给外界注册 订阅者关系
    @Synchronized
    fun <T> with(key: String, type: Class<T>, isStick: Boolean = true) : BusMutableLiveData<T> {
        if (!bus.containsKey(key)) {
            bus[key] = BusMutableLiveData(isStick)
        }
        return bus[key] as BusMutableLiveData<T>
    }

    // Any? 是 Object ,  * 星投影 KT泛型的?  有点像 Java ?
    class BusMutableLiveData<T> private constructor() : MutableLiveData<T>() {

        var isStick: Boolean = false

        // 次构造
        constructor(isStick: Boolean) : this() {
            this.isStick = isStick
        }

        // 我是先执行
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(owner, observer) // 这句会执行父类的  // 启用系统的功能  不写就破坏了

            if (!isStick) {
                hook(observer = observer)
                Log.d("derry", "Kotlin版本的 不启用黏性")
            } else {
                Log.d("derry", "Kotlin版本的 启用黏性")
            }
        }

        private fun hook(observer: Observer<in T>) {
            // TODO 1.得到mLastVersion
            // 获取到LivData的类中的mObservers对象
            val liveDataClass = LiveData::class.java

            val mObserversField: Field = liveDataClass.getDeclaredField("mObservers")
            // 私有修饰也可以访问
            mObserversField.isAccessible = true

            // 获取到这个成员变量的对象  Any == Object
            val mObserversObject: Any = mObserversField.get(this)

            // 得到map对象的class对象   private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
            val mObserversClass: Class<*> = mObserversObject.javaClass

            // 获取到mObservers对象的get方法   protected Entry<K, V> get(K k) {
            val get: Method = mObserversClass.getDeclaredMethod("get", Any::class.java)
            // 私有修饰也可以访问
            get.isAccessible = true

            // 执行get方法
            val invokeEntry: Any = get.invoke(mObserversObject, observer)

            // 取到entry中的value   is "AAA" is String    is是判断类型 as是转换类型
            var observerWraper: Any? = null
            if (invokeEntry != null && invokeEntry is Map.Entry<*, *>) {
                observerWraper = invokeEntry.value
            }
            if (observerWraper == null) {
                throw NullPointerException("observerWraper is null")
            }

            // 得到observerWraperr的类对象
            val supperClass: Class<*> = observerWraper.javaClass.superclass
            val mLastVersion: Field = supperClass.getDeclaredField("mLastVersion")
            mLastVersion.isAccessible = true

            // TODO 2.得到mVersion
            val mVersion: Field = liveDataClass.getDeclaredField("mVersion")
            mVersion.isAccessible = true

            // TODO 3.mLastVersion=mVersion
            val mVersionValue: Any = mVersion.get(this)
            mLastVersion.set(observerWraper, mVersionValue)
        }
    }
}

使用方式:

js 复制代码

源码解析

粘性数据的由来

LiveData为为什么能够观察数据的变化,这与他实现LifecycleBoundObserver是分不开的。实现这个接口,让他又能能够感知被观察者生命周期的能力。 在LiveData中有下边这个重要的函数,状态的变化分发都是通过他进行的

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

shouldBeActive判断了当前被观察者(Activity, Fragment)的生命周期至少是STARTED才会继续进行数据的分发

scss 复制代码
boolean shouldBeActive() {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

dispatchingValue来实现真正的数据分发。

scss 复制代码
void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive
    // owner
    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();
    }
    if (mActive) {
        dispatchingValue(this);
    }
}

dispatchingValue进入后

ini 复制代码
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        // 该值为空的时候,走的是setValue和postValue,不为空的时候,是在状态变化的时候进行的分发
        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;
}

considerNotify又进行了状态检测,这是为什么呢?因为页面生命周期变化可能很快,我们要实时的检测他的状态。

kotlin 复制代码
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;
    }
    // 决定黏性事件的执行与非执行,如果我们先设置了值,那么这里的mLastVersion会在setvalue的时候执行++,导致这两个值不相等,进而会走分发。去掉粘性也很简单,只需要通过反射让这两个值相等就行了。
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}

setValue与postValue

postValue

ini 复制代码
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
ini 复制代码
private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        setValue((T) newValue);
    }
};
typescript 复制代码
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

可见postValue与setValue的区别就是一个线程的切换。因此如果你需要在子线程使用livedata,需要用postValue来实现更新。

再次回调dispatchingValue,这次的initiator事空值,为什么else分支里边是一个循环呢?这是因为对于同一个变量,可能有多个观察者,我们需要通过遍历将所有的值分发给观察者。

ini 复制代码
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;
}

测试源码:github.com/xingchaozha...

相关推荐
栈老师不回家14 分钟前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙20 分钟前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠24 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds44 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm