LiveData粘性数据自定义
-
关键词:kotlin,java,hook,反射,函数默认参数,数据粘性(倒灌)
-
摘要:
- LiveData所维护的数据具有粘性(先修改,后订阅,依然可以收到之前的数据),本例采用kotlin与自定义hook函数,提托反射机制动态修改LiveData源代码实现带开关(由程序猿决定是否启动粘性)的LiveData
- 工程结构:
- 粘性数据的应用场景:需要使用历史数据就直接使用MutableLiveData,但是粘性一般是需要被剔除的(极有可能导致问题)
代码示意:
-
OkLiveDataBusKT
-
实现细节:
-
整体概述:
-
单例模式 去掉黏性事件(有关闭黏性的开关) KT的版本
根据函数默认参数,可以由用户指定是否去掉粘性数据
-
-
为什么要采用单例
object OKLiveDataBusKT { 复制代码
- LiveData常作为工具类使用,代码抽离,让工程结构清晰
-
为什么要去封装自己的总线:维护一个Map
private val bus : MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap() } 复制代码
-
在使用LiveData时,一般使用经由装饰器模式改造后的LiveData—>MutableLiveData(LiveData的子类,内部仅提供setValue与postValue)
-
好处:简化LiveData,代码可追逐性强,使用方便
-
缺点:
- 使用MutableLiveData存放订阅者,自己封装总线维护数据时,是以数据为单位的;
- 换而言之,一个MutableLiveData只能维护一条数据; 如果需要对多条数据维护,那么就需要引入MutableMap;
- 并且受泛型限制,需要传入所维护数据的泛型;
-
-
为什么要使用Map:可以维护多条类型不同的数据
-
key:指代所需要维护的数据
-
指代需要维护的数据,就是在这个上面加眼睛的:MyLiveData.info.observer(this,{ it(指代了所需要维护的数据)
}
-
-
value:存放所维护的具体数据
-
value< Any >:为什么要维护Any
- 对MutableLiveData进行二次封装原来的MutableLiveData在维护数据时,需要传入所维护的数据类型;因为现在是对一组数据进行维护,每条数据的类型不确定,所以采用Any
-
-
为什么要使用懒加载:
- 这个是kotlin中的特性,需要时才加载,节省资源
-
为什么懒加载的是一个HashMap:
- 这个Map本质就是HashMap的二次封装
-
-
为什么要对MutableLiveData进行二次封装:
class BusMutableLiveData<T> private constructor() : MutableLiveData<T>() { 复制代码
- 我们希望实现一个带有数据粘性开关的MutableLiveData
-
为什么要传入两个泛型还要继承MutableLiveData:
- 我们还是采用MutableLiveData,不同点在于,我们将所维护的一组(可以是不同类型)MutableLiveData放到了一个HashMap里面去了;并且当前面的泛型确定,后面的也会跟着改变
- 继承MutableLiveData:使数据可以被LiveData所维护,还是可以实现MyLiveData.info.observer(this,{it})的能力
-
为什么要私有化构造方法:
- 依然保证其全局单例性,保证外界不能入侵代码,只能在这个工具单例中,进行初始化
-
为什么要显示写出此类的次构造函数
// 次构造 constructor(isStick: Boolean) : this() { this.isStick = isStick } 复制代码
- 任何一个函数都有主构造函数(默认的),但是当指定了此构造函数,那么就需要在次构造中调用主构造函数
-
为什么要重写observe方法
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { 复制代码
- 重写系统源码,在调用系统observe的时候,先执行重载的函数
-
为什么需要动态修改LiveData源代码
private fun hook(observer: Observer<in T>) { 复制代码
-
使用反射动态修改LiveData源码去掉数据粘性:让LiveData在被观察者中维护的数据的版本号等于其在观察者中的版本号 核心代码:就是去执行这个if语句,就去掉粘性了,
if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; //noinspection unchecked observer.mObserver.onChanged((T) mData); 复制代码
-
继承关系:
BusMutableLiveData—》MutableLiveData—》LiveData
-
-
-
-
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 如果LiveData默认的黏性,会产生Bug,你就需要剔除黏性
// livedata维护具体的数据
OKLiveDataBusKT.with("data1", String::class.java,false).value = "粘性数据开启,kotlin版本"
OKLiveDataBusJava.getInstance().with("data2", String::class.java).value= "粘性数据,Java版本" // Java版本
// 点击事件,跳转下一个Activity
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
startActivity(Intent(this, MainActivity2::class.java))
}
}
}
复制代码
-
MainActivity2
package com.derry.livedatabusandviewbinding.simple1 import android.os.Bundle import android.util.Log import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.derry.livedatabusandviewbinding.R import kotlin.concurrent.thread class MainActivity2 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) // Kotlin版本订阅观察者,这个key 相当于是LiveData中所维护的数据的唯一标识符,所以在哪里都要一样 OKLiveDataBusKT.with("data1", String::class.java).observe(this, { Toast.makeText(this, "Kotlin版本的 观察者:${it}", Toast.LENGTH_SHORT).show() }) // Java版本订阅观察者(Java是模拟就剔除黏性的,你自己增加开关) OKLiveDataBusJava.getInstance().with("data2", String::class.java).observe(this, { Toast.makeText(this, "Java版本的 观察者:${it}", Toast.LENGTH_SHORT).show() }) } } 复制代码
-
OKLiveDataBusJava
package com.derry.livedatabusandviewbinding.simple1; import androidx.annotation.NonNull; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * 单例模式的 默认是去掉粘性,Java版本;想要开关就自己去加 */ public class OKLiveDataBusJava { // 存放订阅者 private Map<String, BusMutableLiveData<Object>> bus; private static OKLiveDataBusJava liveDataBus = new OKLiveDataBusJava(); private OKLiveDataBusJava() { bus = new HashMap<>(); } public static OKLiveDataBusJava getInstance() { return liveDataBus; } // 注册订阅者 public synchronized <T> BusMutableLiveData<T> with(String key, Class<T> type) { if (!bus.containsKey(key)) { bus.put(key, new BusMutableLiveData<>()); } return (BusMutableLiveData<T>) bus.get(key); } /*public <T> MutableLiveData<T> with(String target, Class<T> type) { if (!bus.containsKey(target)) { bus.put(target, new MutableLiveData<>()); } return (MutableLiveData<T>) bus.get(target); }*/ // 注册订阅者 重载 /*public synchronized BusMutableLiveData<Object> with(String target) { return with(target, Object.class); }*/ public static class BusMutableLiveData<T> extends MutableLiveData<T> { @Override public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) { super.observe(owner, observer); // 启用系统的功能 不写就破坏了 hook(observer); } private void hook(Observer<? super T> observer) { try { // TODO 1.得到mLastVersion // 获取到LivData的类中的mObservers对象 Class<LiveData> liveDataClass = LiveData.class; Field mObserversField = liveDataClass.getDeclaredField("mObservers"); mObserversField.setAccessible(true); // 获取到这个成员变量的对象 Object mObserversObject = mObserversField.get(this); // 得到map对象的class对象 Class<?> mObserversClass = mObserversObject.getClass(); // 获取到mObservers对象的get方法 Method get = mObserversClass.getDeclaredMethod("get", Object.class); get.setAccessible(true); // 执行get方法 Object invokeEntry = get.invoke(mObserversObject, observer); // 取到entry中的value Object observerWraper = null; if (invokeEntry != null && invokeEntry instanceof Map.Entry) { observerWraper = ((Map.Entry) invokeEntry).getValue(); } if (observerWraper == null) { throw new NullPointerException("observerWraper is null"); } // 得到observerWraperr的类对象 Class<?> supperClass = observerWraper.getClass().getSuperclass(); Field mLastVersion = supperClass.getDeclaredField("mLastVersion"); mLastVersion.setAccessible(true); // TODO 2.得到mVersion Field mVersion = liveDataClass.getDeclaredField("mVersion"); mVersion.setAccessible(true); // TODO 3.mLastVersion=mVersion Object mVersionValue = mVersion.get(this); mLastVersion.set(observerWraper, mVersionValue); } catch (Exception e) { e.printStackTrace(); } } } } 复制代码