LiveData粘性数据自定义

1,113 阅读7分钟

LiveData粘性数据自定义

  • 关键词:kotlin,java,hook,反射,函数默认参数,数据粘性(倒灌)

  • 摘要:

    • LiveData所维护的数据具有粘性(先修改,后订阅,依然可以收到之前的数据),本例采用kotlin与自定义hook函数,提托反射机制动态修改LiveData源代码实现带开关(由程序猿决定是否启动粘性)的LiveData
  • 工程结构:

图片.png

  • 粘性数据的应用场景:需要使用历史数据就直接使用MutableLiveData,但是粘性一般是需要被剔除的(极有可能导致问题)

代码示意:

  • OkLiveDataBusKT

    • 实现细节:

      1. 整体概述:

        • 单例模式 去掉黏性事件(有关闭黏性的开关) KT的版本

          根据函数默认参数,可以由用户指定是否去掉粘性数据

      1. 为什么要采用单例

          object OKLiveDataBusKT {
        
        • LiveData常作为工具类使用,代码抽离,让工程结构清晰
      1. 为什么要去封装自己的总线:维护一个Map

           private val bus : MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap() }
        
        • 在使用LiveData时,一般使用经由装饰器模式改造后的LiveData--->MutableLiveData(LiveData的子类,内部仅提供setValue与postValue)

          • 好处:简化LiveData,代码可追逐性强,使用方便

          • 缺点:

            1. 使用MutableLiveData存放订阅者,自己封装总线维护数据时,是以数据为单位的;
            2. 换而言之,一个MutableLiveData只能维护一条数据; 如果需要对多条数据维护,那么就需要引入MutableMap;
            3. 并且受泛型限制,需要传入所维护数据的泛型;
        • 为什么要使用Map:可以维护多条类型不同的数据

          • key:指代所需要维护的数据

            • 指代需要维护的数据,就是在这个上面加眼睛的:MyLiveData.info.observer(this,{ it(指代了所需要维护的数据)

              }

          • value:存放所维护的具体数据

          • value< Any >:为什么要维护Any

            • 对MutableLiveData进行二次封装原来的MutableLiveData在维护数据时,需要传入所维护的数据类型;因为现在是对一组数据进行维护,每条数据的类型不确定,所以采用Any
        • 为什么要使用懒加载:

          • 这个是kotlin中的特性,需要时才加载,节省资源
        • 为什么懒加载的是一个HashMap:

          • 这个Map本质就是HashMap的二次封装
      2. 为什么要对MutableLiveData进行二次封装:

         class BusMutableLiveData<T> private constructor() : MutableLiveData<T>() {
        
        • 我们希望实现一个带有数据粘性开关的MutableLiveData
      3. 为什么要传入两个泛型还要继承MutableLiveData:

        • 我们还是采用MutableLiveData,不同点在于,我们将所维护的一组(可以是不同类型)MutableLiveData放到了一个HashMap里面去了;并且当前面的泛型确定,后面的也会跟着改变
        • 继承MutableLiveData:使数据可以被LiveData所维护,还是可以实现MyLiveData.info.observer(this,{it})的能力
      4. 为什么要私有化构造方法:

        • 依然保证其全局单例性,保证外界不能入侵代码,只能在这个工具单例中,进行初始化
      5. 为什么要显示写出此类的次构造函数

           // 次构造
                  constructor(isStick: Boolean) : this() {
                      this.isStick = isStick
                  }
        
        • 任何一个函数都有主构造函数(默认的),但是当指定了此构造函数,那么就需要在次构造中调用主构造函数
      6. 为什么要重写observe方法

         override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        
        • 重写系统源码,在调用系统observe的时候,先执行重载的函数
      7. 为什么需要动态修改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();
                  }
              }
          }
      }