LiveDataBus消息事件总线之二-(不含反射和hook)

LiveDataBus消息事件总线之一

LiveData的介绍

LiveData是Android Architecture Components提出的框架。它是一个可以被观察的数据持有类,能够感知并遵循Activity、Fragment或Service等组件的生命周期。正是由于LiveData对组件生命周期可感知的特点,因此可以做到仅在组件处于生命周期的激活状态时才更新UI数据。

LiveData需要一个观察者对象,一般是Observer类的具体实现。当观察者的生命周期处于STARTED或RESUMED状态时,LiveData会通知观察者数据变化;在观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。

LiveData的优点

  • UI和实时数据保持一致:因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。
  • 避免内存泄漏:观察者被绑定到组件的生命周期上,当被绑定的组件销毁(destroy)时,观察者会立刻自动清理自身的数据。
  • 不会再产生由于Activity处于stop状态而引起的崩溃:例如,当Activity处于后台状态时,是不会收到LiveData的任何事件的。
  • 不需要再解决生命周期带来的问题:LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。
  • 实时数据刷新:当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据。
  • 解决Configuration Change问题:在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。

重点:为什么使用LiveData构建数据通信总线LiveDataBus

使用LiveData的理由

LiveData具有的这种可观察性和生命周期感知的能力,使其非常适合作为Android通信总线的基础构件。使用者不用显示调用反注册方法。由于LiveData具有生命周期感知能力,所以LiveDataBus只需要调用注册回调方法,而不需要显示的调用反注册方法。这样带来的好处不仅可以编写更少的代码,而且可以完全杜绝其他通信总线类框架(如EventBus、RxBus)忘记调用反注册所带来的内存泄漏的风险。

为什么要用LiveDataBus替代EventBus和RxBus

  1. 实现简单:LiveDataBus的实现极其简单,相对EventBus复杂的实现,LiveDataBus只需要一个类就可以实现。LiveDataBus可以减小APK包的大小,由于LiveDataBus只依赖Android官方Android Architecture Components组件的LiveData,没有其他依赖,本身实现只有一个类。作为比较,EventBus JAR包大小为57kb,RxBus依赖RxJava和RxAndroid,其中RxJava2包大小2.2MB,RxJava1包大小1.1MB,RxAndroid包大小9kb。使用LiveDataBus可以大大减小APK包的大小。
  2. 依赖方支持更好:LiveDataBus只依赖Android官方Android Architecture Components组件的LiveData,相比RxBus依赖的RxJava和RxAndroid,依赖方支持更好。
  3. 具有生命周期感知:LiveDataBus具有生命周期感知,在Android系统中使用调用者不需要调用反注册,相比EventBus和RxBus使用更为方便,并且没有内存泄漏风险。

升级版不含反射和hook

目的为了过谷歌审核

先上代码

kotlin 复制代码
package com.example.livedatabus

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicInteger


/**
 * 不使用反射和Hook的LiveData事件总线实现
 */
class LiveDataBus private constructor() {
    // 存储所有事件通道的Map
    private val bus: MutableMap<String, BusLiveData<*>>

    // 单例模式
    init {
        bus = HashMap()
    }

    private object SingletonHolder {
        val DEFAULT_BUS: LiveDataBus = LiveDataBus()
    }

    /**
     * 获取指定类型的事件通道
     * @param key 事件唯一标识
     * @param type 事件数据类型,用于类型安全检查
     * @return 对应类型的事件通道,非空
     */
    @Synchronized
    fun <T> with(key: String, type: Class<T>): BusLiveData<T> {
        var liveData: BusLiveData<T>? = null
        try {
            // 先尝试获取已存在的通道
            liveData = bus[key] as BusLiveData<T>?

            // 如果不存在则创建新的
            if (liveData == null) {
                liveData = BusLiveData<T>()
                bus[key] = liveData
            } else {
                // 检查已存在的通道类型是否与请求类型一致
                // 通过尝试转换来验证类型兼容性
                try {
                    // 这里利用类型擦除的特性进行检查
                    @Suppress("UNCHECKED_CAST")
                    val check = liveData as BusLiveData<*>
                    if (check.type != null && check.type != type) {
                        throw IllegalArgumentException("Type mismatch for key '$key': existing type is ${check.type}, requested type is $type")
                    }
                } catch (e: ClassCastException) {
                    throw IllegalArgumentException("Type mismatch for key '$key'", e)
                }
            }

            // 记录当前通道的类型
            liveData.type = type
        } catch (e: ClassCastException) {
            throw IllegalArgumentException("Type mismatch for key '$key'", e)
        }
        return liveData
    }

    /**
     * 获取默认类型的事件通道
     */
    fun with(key: String): BusLiveData<Any> {
        return with(key, Any::class.java)
    }

    /**
     * 自定义LiveData,通过版本控制解决粘性事件问题
     */
    class BusLiveData<T> : MutableLiveData<T>() {

        // 用于记录当前通道的数据类型
        internal var type: Class<*>? = null

        // 事件版本号,原子类保证线程安全
        private val mVersion = AtomicInteger(0)

        // 存储观察者包装类,用于移除观察者
        private val observerMap: MutableMap<Observer<*>, ObserverWrapper<T>> =
            HashMap<Observer<*>, ObserverWrapper<T>>()

        override fun setValue(value: T) {
            // 每次发送事件都递增版本号
            mVersion.incrementAndGet()
            super.setValue(value)
        }

        override fun postValue(value: T) {
            // 异步发送事件同样递增版本号
            mVersion.incrementAndGet()
            super.postValue(value)
        }

        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            // 包装原始观察者,记录注册时的版本号
            val wrapper: ObserverWrapper<T> = ObserverWrapper<T>(observer, mVersion.get())
            observerMap[observer] = wrapper
            super.observe(owner, wrapper)
        }

        override fun observeForever(observer: Observer<in T>) {
            val wrapper: ObserverWrapper<T> = ObserverWrapper<T>(observer, mVersion.get())
            observerMap[observer] = wrapper
            super.observeForever(wrapper)
        }

        override fun removeObserver(observer: Observer<in T>) {
            // 移除对应的包装类观察者
            val wrapper: ObserverWrapper<T>? = observerMap.remove(observer)
            if (wrapper != null) {
                super.removeObserver(wrapper)
            } else {
                super.removeObserver(observer)
            }
        }

        /**
         * 观察者包装类,用于版本控制
         */
        private inner class ObserverWrapper<T>(
            private val mObserver: Observer<in T>,
            private var mLastVersion: Int
        ) : Observer<T> {
            override fun onChanged(t: T) {
                if (mLastVersion < mVersion.get()) {
                    mLastVersion = mVersion.get()
                    mObserver.onChanged(t)
                }
            }
        }
    }

    companion object {
        fun get(): LiveDataBus {
            return SingletonHolder.DEFAULT_BUS
        }
    }
}
相关推荐
_祝你今天愉快35 分钟前
Java-JVM探析
android·java·jvm
飞天卡兹克1 小时前
forceStop流程会把对应进程的pendingIntent给cancel掉
android
Monkey-旭9 小时前
Android Bitmap 完全指南:从基础到高级优化
android·java·人工智能·计算机视觉·kotlin·位图·bitmap
Mike_Wuzy14 小时前
【Android】发展历程
android
开酒不喝车14 小时前
安卓Gradle总结
android
阿华的代码王国15 小时前
【Android】PopupWindow实现长按菜单
android·xml·java·前端·后端
稻草人不怕疼16 小时前
Android 15 全屏模式适配:A15TopView 自定义组件分享
android
~央千澈~17 小时前
05百融云策略引擎项目交付-laravel实战完整交付定义常量分文件配置-独立建立lib类处理-成功导出pdf-优雅草卓伊凡
android·laravel·软件开发·金融策略
_一条咸鱼_17 小时前
Android Runtime冷启动与热启动差异源码级分析(99)
android·面试·android jetpack