当前位置: 首页 > news >正文

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

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

这里给出一个实现方案。

interface ICrossNotify<T> {@MainThreadfun 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 = trueif (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.sizewhile (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 = falseif (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 就认为它是相同的,就允许覆盖。

简易好用。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 苏州金龙海格汽车入选2024中国汽车行业可持续发展实践案例
  • 项目管理_XX市XX区人民医院HRP信息系统建设项目(整体管理)实例
  • 数据可视化配色新工具,颜色盘多达2500+类
  • 云计算实训09——rsync远程同步、自动化推取文件、对rsyncd服务进行加密操作、远程监控脚本
  • AI开源战争的真相
  • 生产力工具|Endnote 21 Macwin版本安装
  • 一个非常好的美图展示网站整站打包源码,集成了wordpress和开源版ripro主题,可以完美运营。
  • VBA中如何使用Edge内核Browser?
  • 【工具使用】EMACS的verilog_mode脚本
  • 告别自动激活:掌握如何在Conda中禁用Base环境
  • androidTest 与 Test详解
  • 题解|2024暑期杭电多校01
  • python环境搭建步骤记录
  • C语言 | Leetcode C语言题解之第237题删除链表中的节点
  • B树(B-Tree)数据结构
  • 07.Android之多媒体问题
  • echarts的各种常用效果展示
  • github从入门到放弃(1)
  • httpie使用详解
  • Koa2 之文件上传下载
  • php面试题 汇集2
  • Vue 重置组件到初始状态
  • Vue2.x学习三:事件处理生命周期钩子
  • win10下安装mysql5.7
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 简单基于spring的redis配置(单机和集群模式)
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 入手阿里云新服务器的部署NODE
  • 手写双向链表LinkedList的几个常用功能
  • 手写一个CommonJS打包工具(一)
  • 听说你叫Java(二)–Servlet请求
  • 微信小程序:实现悬浮返回和分享按钮
  • 微信小程序设置上一页数据
  • 问题之ssh中Host key verification failed的解决
  • 我这样减少了26.5M Java内存!
  • 7行Python代码的人脸识别
  • const的用法,特别是用在函数前面与后面的区别
  • MyCAT水平分库
  • mysql面试题分组并合并列
  • ​力扣解法汇总946-验证栈序列
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #QT(QCharts绘制曲线)
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (转)fock函数详解
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • .apk 成为历史!
  • .net MySql
  • .NET面试题(二)
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • /*在DataTable中更新、删除数据*/
  • [17]JAVAEE-HTTP协议
  • [20190401]关于semtimedop函数调用.txt
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [Arduino学习] ESP8266读取DHT11数字温湿度传感器数据