fix: 红点不显示——SharedFlow(replay=0)事件竞争

根因:EventBus用MutableSharedFlow(replay=0,buffer=0),
MainActivity和HomeFragment同时collect,NewTaskArrived
中间事件在emit时挂起(无缓冲),HomeFragment收不到。

修复:去掉NewTaskArrived中间事件,HomeFragment直接在
MqttMessageReceived type=1中处理:
1. 调 notificationManager.onNewTaskMessage()
2. 直接调 activity.showNotificationBanner() 显示横幅
3. fetchStatistics(checkDots=true) 刷新红点

架构简化:
- MainActivity不再监听MQTT type=1(去掉observeMqttMessages)
- NotificationManager不再emit事件(去掉EventBus依赖)
- 去抖合并后通过回调onPendingCountChanged通知UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-04-29 18:48:05 +09:30
parent 73af1cbfa9
commit 0f6385a9fe
3 changed files with 36 additions and 60 deletions

View File

@@ -7,16 +7,9 @@ import android.view.View
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.xiaoqu.watch.databinding.ActivityMainBinding import com.xiaoqu.watch.databinding.ActivityMainBinding
import com.xiaoqu.watch.event.AppEvent
import com.xiaoqu.watch.event.EventBus
import com.xiaoqu.watch.service.manager.NotificationManager
import com.xiaoqu.watch.service.manager.SystemStateMonitor import com.xiaoqu.watch.service.manager.SystemStateMonitor
import com.xiaoqu.watch.ui.widget.NotificationBannerView import com.xiaoqu.watch.ui.widget.NotificationBannerView
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@@ -32,17 +25,9 @@ class MainActivity : AppCompatActivity() {
/** 系统状态监听器(电量、蓝牙状态) */ /** 系统状态监听器(电量、蓝牙状态) */
@Inject lateinit var systemStateMonitor: SystemStateMonitor @Inject lateinit var systemStateMonitor: SystemStateMonitor
/** 消息通知管理器 */ /** 通知横幅HomeFragment 需访问) */
@Inject lateinit var notificationManager: NotificationManager
/** 事件总线 */
@Inject lateinit var eventBus: EventBus
/** 通知横幅HomeFragment 需访问设置点击回调) */
lateinit var notificationBanner: NotificationBannerView lateinit var notificationBanner: NotificationBannerView
/** Activity 协程作用域 */
private val activityScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -68,9 +53,6 @@ class MainActivity : AppCompatActivity() {
// 初始化通知横幅 // 初始化通知横幅
notificationBanner = binding.notificationBanner notificationBanner = binding.notificationBanner
// 监听 MQTT 新任务消息,显示横幅
observeMqttMessages()
Timber.d("MainActivity created") Timber.d("MainActivity created")
} }
@@ -80,27 +62,12 @@ class MainActivity : AppCompatActivity() {
notificationBanner.destroy() notificationBanner.destroy()
} }
// ===== MQTT 新任务 → 通知横幅 ===== // ===== 通知横幅(由 HomeFragment 调用显示) =====
/** 监听 MQTT 消息和通知事件 */ /** 显示通知横幅(供 HomeFragment 调用) */
private fun observeMqttMessages() { fun showNotificationBanner(count: Int) {
activityScope.launch { notificationBanner.show(count)
eventBus.events.collect { event -> }
when (event) {
is AppEvent.MqttMessageReceived -> {
if (event.type == 1) {
// 交给 NotificationManager 处理(去抖+合并+震动+亮屏)
notificationManager.onNewTaskMessage(event.rawJson)
}
}
// NotificationManager 处理完后发出事件 → 显示/更新横幅
is AppEvent.NewTaskArrived -> {
notificationBanner.show(event.count)
}
else -> {}
}
}
}
} }
// ===== 下拉手势检测Activity 级别,不可能被子 View 拦截) ===== // ===== 下拉手势检测Activity 级别,不可能被子 View 拦截) =====

View File

@@ -4,8 +4,6 @@ import com.xiaoqu.watch.data.task.TaskStatistics
import com.xiaoqu.watch.device.screen.ScreenController import com.xiaoqu.watch.device.screen.ScreenController
import com.xiaoqu.watch.device.sensor.VibrationController import com.xiaoqu.watch.device.sensor.VibrationController
import com.xiaoqu.watch.device.sensor.VibrationDefaults import com.xiaoqu.watch.device.sensor.VibrationDefaults
import com.xiaoqu.watch.event.AppEvent
import com.xiaoqu.watch.event.EventBus
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@@ -27,8 +25,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class NotificationManager @Inject constructor( class NotificationManager @Inject constructor(
private val vibrationController: VibrationController, private val vibrationController: VibrationController,
private val screenController: ScreenController, private val screenController: ScreenController
private val eventBus: EventBus
) { ) {
companion object { companion object {
/** 去抖间隔(毫秒) */ /** 去抖间隔(毫秒) */
@@ -73,6 +70,9 @@ class NotificationManager @Inject constructor(
/** 协程作用域 */ /** 协程作用域 */
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
/** 去抖合并后的回调(通知 UI 更新横幅数字) */
var onPendingCountChanged: ((Int) -> Unit)? = null
/** /**
* 处理 MQTT type=1 新任务消息 * 处理 MQTT type=1 新任务消息
* 去抖策略1s 窗口内的消息暂存,窗口结束后合并处理(不丢弃) * 去抖策略1s 窗口内的消息暂存,窗口结束后合并处理(不丢弃)
@@ -104,8 +104,8 @@ class NotificationManager @Inject constructor(
for (json in jsons) { for (json in jsons) {
processMessageSilent(json) // 只加 ID不重复震动 processMessageSilent(json) // 只加 ID不重复震动
} }
// 合并后统一发一次事件 // 合并后通知 UI 更新数字
notifyUi() onPendingCountChanged?.invoke(pendingCount)
} }
} }
} }
@@ -128,8 +128,7 @@ class NotificationManager @Inject constructor(
vibrationController.executePattern(pattern) vibrationController.executePattern(pattern)
} }
screenController.turnOn() screenController.turnOn()
// UI 更新(横幅+红点)由调用方处理,不再通过 EventBus
notifyUi()
} }
/** 静默处理(只加 ID不震动不亮屏用于合并暂存消息 */ /** 静默处理(只加 ID不震动不亮屏用于合并暂存消息 */
@@ -149,12 +148,7 @@ class NotificationManager @Inject constructor(
Timber.d("通知: 当前未读 ${_pendingTaskIds.size}") Timber.d("通知: 当前未读 ${_pendingTaskIds.size}")
} }
/** 通知 UI 更新(横幅+红点) */ // notifyUi 已移除UI 更新改由调用方HomeFragment直接处理
private fun notifyUi() {
scope.launch {
eventBus.emit(AppEvent.NewTaskArrived(_pendingTaskIds.toList(), _pendingTaskIds.size))
}
}
/** 消费所有未读消息(用户点击提示条/横幅查看全部) */ /** 消费所有未读消息(用户点击提示条/横幅查看全部) */
fun consumeAll() { fun consumeAll() {

View File

@@ -130,6 +130,12 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
// 监听打卡状态 // 监听打卡状态
observePunchState() observePunchState()
// 去抖合并后更新横幅数字
notificationManager.onPendingCountChanged = { count ->
(activity as? com.xiaoqu.watch.app.MainActivity)?.showNotificationBanner(count)
fetchStatistics(checkDots = true)
}
} }
override fun onResume() { override fun onResume() {
@@ -175,12 +181,13 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
// 清理 Activity 回调,避免泄漏 // 清理回调,避免泄漏
(activity as? com.xiaoqu.watch.app.MainActivity)?.let { (activity as? com.xiaoqu.watch.app.MainActivity)?.let {
it.onSwipeDown = null it.onSwipeDown = null
it.onBackKeyPressed = null it.onBackKeyPressed = null
it.notificationBanner.onClick = null it.notificationBanner.onClick = null
} }
notificationManager.onPendingCountChanged = null
} }
// ===== 打卡面板 ===== // ===== 打卡面板 =====
@@ -484,12 +491,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
is AppEvent.BluetoothStateChanged -> { is AppEvent.BluetoothStateChanged -> {
statusBar.updateBluetooth(event.isOn) statusBar.updateBluetooth(event.isOn)
} }
// 新任务到达 → 刷新统计 + 累积红点
is AppEvent.NewTaskArrived -> {
Timber.d("首页: 新任务到达 (${event.count} 条)")
fetchStatistics(checkDots = true)
setupBannerClick()
}
// MQTT 消息 // MQTT 消息
is AppEvent.MqttMessageReceived -> { is AppEvent.MqttMessageReceived -> {
when (event.type) { when (event.type) {
@@ -498,6 +499,20 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
Timber.d("首页: 收到日常动态") Timber.d("首页: 收到日常动态")
fetchStatistics() fetchStatistics()
} }
1 -> {
// 新任务 → 直接处理(不走中间事件,避免 SharedFlow 竞争)
Timber.d("首页: 收到新任务通知")
notificationManager.onNewTaskMessage(event.rawJson)
// 显示横幅
val count = notificationManager.pendingCount
if (count > 0) {
(activity as? com.xiaoqu.watch.app.MainActivity)
?.showNotificationBanner(count)
}
// 刷新统计 + 红点
fetchStatistics(checkDots = true)
setupBannerClick()
}
3 -> { 3 -> {
// 解绑 → 停止蓝牙 → 清除数据 → 跳绑定页 // 解绑 → 停止蓝牙 → 清除数据 → 跳绑定页
Timber.d("首页: 收到解绑消息") Timber.d("首页: 收到解绑消息")