自学内容网 自学内容网

一种全局数据变化而且是多个的通知实现

有个需求:
从activityA,打开activityB, activityC 或者还存在viewpager上的其他Fragment。甚至activityB,又打开了activityBA。
在这些界面上,大家都有相同的数据Bean(name, info, isFavourite)去展示成卡片列表。
这种情况下,当操作了某个界面的Bean数据,更新当前自己是很容易的;但是当想更新其他界面,其他activity就比较麻烦了。

这里给出一个实现方案。

interface ICrossNotify<T> {
    @MainThread
    fun onCrossNotify(data:List<T>?)
}
 
/**
 * @date :2024/7/18 19:34
 * @description: 跨activity通知。
 * 必须把它变成一个单例(或存储在单例里面,唯一。但可以多个实例。)
 *
 * 用法在Activity或者Fragment
 * 1. onCreate()里面调用callOnCreate(this)
 * 2. 然后,给它实现ICrossNotify接口。一切就已经完成。
 *
 * 更新数据,调用changeData,必须保证是主线程。
 *
 * 内部会针对,监听的前后,分别通知不同的时机的数据。
 */
class CrossActivityNotifyListObserver<T : Any>(private val mainHandler:Handler) : DefaultLifecycleObserver{
    //value的左边代表是否监听后,有更新过。右边代表更新后的值。
    private data class CrossActivityNotifyListInfo<T>(var isResumed:Boolean,
                                              val data: ArrayList<T> = ArrayList(4))

    private val TAG = "crossNotify"

    private val callbackList = HashMap<LifecycleOwner, CrossActivityNotifyListInfo<T>>(8)

    /**
     * 如果你希望数据进行去重,比如Info(a, b, c),其中以a字段如果相同就认为是同一份数据,
     * 本类中将会进行去重。避免多次操作同一个对象。
     */
    var distinct:((a:T, b:T)->Boolean)? = null

    /**
     * 每次resume都调用本函数。内部做了处理。
     */
    fun callOnCreate(owner: LifecycleOwner) {
        //如果不存在,就将入监听列表
        if (owner !is ICrossNotify<*>) {
            throw IllegalArgumentException("Do not call callOnCreate() because owner is not ICrossNotify!")
        }
        owner.lifecycle.addObserver(this)
        callbackList[owner] = CrossActivityNotifyListInfo(isResumed = true)
        if (BuildConfig.DEBUG) Log.d(TAG, "callOnCreate add owner $owner")
    }

    /**
     * 暂定必须,不得为空。
     * 会帮你运行在主线程。
     */
    fun notify(d:T) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "changeData $d")
        }
        if (!isMainThread) {
            mainHandler.post {
                changeDataMainThread(d)
            }
        } else {
            changeDataMainThread(d)
        }
    }

    private fun changeDataMainThread(d: T) {
        callbackList.forEach { (owner, info) ->
            addWithDistinct(info, d)

            if (info.isResumed) {
                if (BuildConfig.DEBUG) Log.d(TAG, "changeData owner $owner isResumed onDateChanged")
                owner.asOrNull<ICrossNotify<T>>()?.onCrossNotify(fetchAndClear(info))
            } else {
                if (BuildConfig.DEBUG) Log.d(TAG, "changeData owner $owner isNotResumed just save")
            }
        }
    }

    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        //如果已经存在,就把当前的通知出来。
        val info = callbackList[owner]!!
        info.isResumed = true
        if (info.data.isNotEmpty()) {
            if (BuildConfig.DEBUG) Log.d(TAG, "onResumeCall exist $owner onDataChanged ${info.data}")
            val outList = fetchAndClear(info)
            owner.asOrNull<ICrossNotify<T>>()?.onCrossNotify(outList)
        } else {
            if (BuildConfig.DEBUG) Log.d(TAG, "onResumeCall exist $owner no change.")
        }
    }

    private fun addWithDistinct(info: CrossActivityNotifyListInfo<T>, data:T) {
        distinct?.let { dist->
            var size = info.data.size
            while (size > 0) { //倒序遍历移除
                size--
                if (dist(info.data[size], data)) {
                    info.data.removeAt(size)
                }
            }
        }

        info.data.add(data)
    }

    private fun fetchAndClear(info: CrossActivityNotifyListInfo<T>): List<T>? {
        if (info.data.isEmpty()) {
            return null
        }
        val outList = ArrayList<T>(info.data)
        info.data.clear()

        return outList
    }

    override fun onPause(owner: LifecycleOwner) {
        super.onPause(owner)
        callbackList[owner]?.isResumed = false
        if (BuildConfig.DEBUG) Log.d(TAG, "onPause owner $owner")
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        callbackList.remove(owner)
        if (BuildConfig.DEBUG) Log.d(TAG, "onDestroy owner remove $owner")
    }
}

在全局的某个单例中定义全局单例变量:


val changeData = CrossActivityNotifyListObserver<Pair<String, Boolean>>(mainHandler).also {
        it.distinct = { a, b->
            a.first == b.first
        }
    }

在更新数据的时候,changeData.notify(bean)。

想监听的activity、fragment,在onCreate函数中调用callOnCreate,并让它实现ICrossNotify即可。

这个简易的框架的目的是当onResume状态下,就把当前通知的单条变化直接通知到;
非resume状态下,就收集后,等待resume一来就发送。

代码设计为避免了粘性数据,即从某个界面监听开始,后续的变化它才会接受到。而且提供了计算去重distinct 的逻辑,比如Bean(name, info, isFavourite), 你可以判断a.name == b.name 就认为它是相同的,就允许覆盖。

简易好用。


原文地址:https://blog.csdn.net/jzlhll123/article/details/140556897

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!