You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RxJava中.zip操作符重复发射相同值问题排查

问题分析与解决方案

首先,你的核心问题出在两个地方:Android MotionEvent的复用机制和BehaviorSubject的缓存特性,其中前者是导致你收到两个相同ACTION_UP事件的直接原因。

1. MotionEvent被系统复用,导致对象内容被覆盖

Android系统为了优化性能,会对MotionEvent对象进行池化复用——也就是说,同一个MotionEvent对象可能会被用来承载不同的触摸事件(比如先承载ACTION_DOWN,之后被修改为ACTION_UP)。当你直接把原始的me对象发送到BehaviorSubject时,后续的触摸事件会覆盖这个对象的内容。

举个实际场景:

  • 触发ACTION_DOWN时,你把me发送到touchDownSource,此时这个对象的action是DOWN;
  • 触发ACTION_UP时,系统复用了同一个me对象,修改它的action为UP,然后你把它发送到touchUpSource;
  • 这时候touchDownSource里缓存的那个MotionEvent对象已经被改成了UP,所以zip操作符拿到的两个事件自然是同一个UP对象。

2. BehaviorSubject的缓存特性可能不符合你的需求

BehaviorSubject会缓存最后一次发送的事件,当有新的事件到来时会自动和缓存的事件配对。如果你的需求是配对每一次ACTION_DOWN和紧随其后的ACTION_UP,这种缓存特性会导致错误配对:比如第一次DOWN-UP配对后,第二次触发DOWN时,touchUpSource缓存的还是上一次的UP事件,zip会立刻把新DOWN和旧UP配对,这显然不是你想要的。

修正后的代码

针对这两个问题,我们可以这样修改:

第一步:复制MotionEvent,避免复用问题

在发送事件到Subject时,使用MotionEvent.obtain(me)创建一个副本,这样原始对象的修改不会影响到Subject中保存的事件:

val touchDownSource = PublishSubject.create<MotionEvent>() // 换成PublishSubject
val touchUpSource = PublishSubject.create<MotionEvent>()

RxView.touches(binding.containerOverlay)
    .subscribe({ me ->
        when (me.action) {
            MotionEvent.ACTION_DOWN -> touchDownSource.onNext(MotionEvent.obtain(me))
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> touchUpSource.onNext(MotionEvent.obtain(me))
        }
    })

第二步:换成PublishSubject,避免错误配对

PublishSubject不会缓存事件,只会发送订阅之后产生的新事件,这样每次DOWN事件都会等待后续的第一个UP事件进行配对,完全符合你的需求。

第三步:回收复制的MotionEvent,避免内存泄漏

因为我们创建了MotionEvent的副本,使用完后需要调用recycle()释放资源:

val zipper = BiFunction<MotionEvent, MotionEvent, Pair<MotionEvent, MotionEvent>>({ v1, v2 -> v1 to v2 })
Observable.zip(touchDownSource, touchUpSource, zipper)
    .filter { Math.abs(it.first.toPoint().length() - it.second.toPoint().length()) < 50 }
    .map { 
        val point = it.second.toPoint()
        // 回收两个MotionEvent副本
        it.first.recycle()
        it.second.recycle()
        point
    }
    .subscribe({ Utils.toast(this, "${it.x}--${it.y}") }, ErrLogger::e)

这样修改后,你就不会再收到两个相同的ACTION_UP事件,而且每一次DOWN都会和对应的UP正确配对啦!

内容的提问来源于stack exchange,提问作者P. Ilyin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:01:36