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
-
-
-
-
完整代码:OKLiveDataBusKT
package com.derry.livedatabusandviewbinding.simple1 import android.util.Log 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 object OKLiveDataBusKT { // 存放订阅者,自己封装总线维护数据时,是以数据为单位的; private val bus : MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap() } // 暴露一个函数,给外界注册 订阅者关系,外界直接订阅就行了 /* 1. 因为上下都是私有的,需要暴露出一个线程 2. 使用同步锁,避免多线程并发安全关系,添加一个注解就行了 3. 传入key,value,第三个参数使用了kotlin中的默认参数 4. 需要判断重复的key 5. 不使用put函数,使用运算符重载 * */ @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 } //tips:在清单文件中不指定启动项,仍然是可以启动应用的,两次hook,欺骗AMS检查 // 我是先执行 override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { super.observe(owner, observer) // 这句会执行父类的 // 启用系统的功能 不写就破坏了 if (!isStick) { hook(observer = observer) //Log.d("OKLiveDataBusKT", "Kotlin版本的 不启用黏性") } else { // Log.d("OKLiveDataBusKT", "Kotlin版本的 启用黏性") } } private fun hook(observer: Observer<in T>) { // TODO 1.得到mLastVersion // 需要转成java的 class val liveDataClass = LiveData::class.java //先拿到字段mObservers // 获取到LivData的类中的mObservers字段(这个里面保存了二次封装的LifecycleBoundObserver), // LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); // ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); val mObserversField: Field = liveDataClass.getDeclaredField("mObservers") mObserversField.isAccessible = true // 私有修饰也可以访问 // 获取到这个成员变量的对象(Any == Object):要想去拿某个字段,或者去拿某哦个函数,那么一定要去拿相应的对象 // private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = // new SafeIterableMap<>(); //就是去拿这个map:SafeIterableMap,这个this代表的是LiveData val mObserversObject: Any = mObserversField.get(this) // 得到map对象的class对象 private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = val mObserversClass: Class<*> = mObserversObject.javaClass // 获取到mObservers对象的get方法(map都会维护一个entry的,都有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) //Any?,才是object, *:这个是星投影是kotlin中的泛型 // 取到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; // 就是对齐了,LiveData所维护的数据在被观察者,观察者中的版本号相同,直接赋值,就好了 val mVersionValue: Any = mVersion.get(this) mLastVersion.set(observerWraper, mVersionValue) } } } -
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(); } } } }