Jetpack系列-LiveData二次封装,去除粘性数据

81 篇文章 1 订阅
1 篇文章 0 订阅

LiveData默认带有粘性数据特征,但是很多情况下并不需要粘性数据,这时候可以通过反射去修改mLastVersion和mVersion的值,使他们相等,就可以去掉粘性数据。

封装一个单例NoStickLiveData。

object NoStickLiveData {

    private val map: MutableMap<String, NoStickMutableLiveData<Any>> by lazy { HashMap() }

    @Synchronized
    fun <T> with(key: String, isStick: Boolean = false): NoStickMutableLiveData<T> {
        if (!map.containsKey(key)) {
            map[key] = NoStickMutableLiveData(isStick)
        }
        return map[key] as NoStickMutableLiveData<T>
    }

    class NoStickMutableLiveData<T> private constructor() :
        MutableLiveData<T>() {

        private 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) {
                solveStick(observer)
            }
        }

        /**
         * 使用反射将mLastVersion的值改为和mVersion相同,就可以解决粘性数据问题
         */
        private fun solveStick(observer: Observer<in T>) {
            val liveDataClass = LiveData::class.java

            //先拿到observer.mLastVersion,mLastVersion在ObserverWrapper中,ObserverWrapper又存放在mObservers中

            //获取mObserversFiled
            val mObserversFiled = liveDataClass.getDeclaredField("mObservers")
            mObserversFiled.isAccessible = true

            //获取map值,this就是LiveData
            val mObserversMap: Any = mObserversFiled.get(this)

            //拿到mObserversMap的class
            val mObserversMapClass = mObserversMap.javaClass

            //获取map的get方法
            val mObserversMapGet = mObserversMapClass.getDeclaredMethod("get", Any::class.java)
            mObserversMapGet.isAccessible = true

            //执行map的get方法,获取ObserverWrapper
            var observerWrapper: Any? = null
            val invokeEntry = mObserversMapGet.invoke(mObserversMap, observer)
            if (invokeEntry != null && invokeEntry is Map.Entry<*, *>) {
                observerWrapper = invokeEntry.value
            } else {
                throw Exception("observerWrapper error")
            }

            //获取ObserverWrapper中的mLastVersion
            //由于这里获取到的的是ObserverWrapper的子类LifecycleBoundObserver或AlwaysActiveObserver
            //所以需要先用superclass获取超类
            val observerWrapperSuperClass = observerWrapper?.javaClass?.superclass
            val mLastVersionFiled = observerWrapperSuperClass?.getDeclaredField("mLastVersion")
            mLastVersionFiled?.isAccessible = true

            //获取mVersion
            val mVersionFiled = liveDataClass.getDeclaredField("mVersion")
            mVersionFiled.isAccessible = true
            //拿到mVersion的值
            val mVersion = mVersionFiled.get(this)

            //将mLastVersion的值设置成跟mVersion相同
            mLastVersionFiled?.set(observerWrapper, mVersion)
        }
    }
}

使用,在NoStick1Activity先修改数据,然后再跳转到NoStick2Activity,可以看到NoStickLiveData.with传true,会正常有粘性数据,传false,就没有粘性数据。

class NoStick1Activity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_no_stick1)

        //参数isStick为tru,表示有粘性
        NoStickLiveData.with<String>("stick_data", true).value = "message----stick_data"
        //参数isStick不传或者为false,表示没有粘性
        NoStickLiveData.with<String>("noStick_data").value = "message----noStick_data"

        findViewById<Button>(R.id.button).setOnClickListener {
            startActivity(Intent(this, NoStick2Activity::class.java))
        }
    }
}

class NoStick2Activity : AppCompatActivity() {

    private val TAG = "NoStick2Activity"

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

        NoStickLiveData.with<String>("stick_data", false).observe(this) {
            Log.d(TAG, "stick_data: $it")
        }
        NoStickLiveData.with<String>("noStick_data").observe(this) {
            Log.d(TAG, "noStick_data: $it")
        }
    }
}

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的小伙伴直接点击文末小卡片免费领取哦,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:点击下方CSDN官方认证卡片👇免费领取

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值