From 93a31e76a38d67ad57175eae85d360d238d4d64c Mon Sep 17 00:00:00 2001 From: dongliang Date: Wed, 29 Apr 2026 19:23:01 +0930 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=A0=B9=E5=9B=A0=E2=80=94?= =?UTF-8?q?=E2=80=94EventBus=E5=8A=A0buffer=E9=98=B2=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1+=E6=81=A2=E5=A4=8D=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:SharedFlow(replay=0,extraBufferCapacity=0)导致emit挂起, 多个collector竞争时事件丢失。之前的补丁越改越乱。 修复: 1. EventBus: extraBufferCapacity=64,emit不再阻塞 2. 恢复正确架构: - MainActivity: 监听MQTT type=1→NotificationManager→横幅 - NotificationManager: 处理后emit NewTaskArrived - HomeFragment: 监听NewTaskArrived→红点+统计 3. StatusBarView: 电池位置恢复原位,默认电量-1,主动查询系统电量 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/com/xiaoqu/watch/app/MainActivity.kt | 32 +++++++++++-- .../java/com/xiaoqu/watch/event/EventBus.kt | 2 +- .../service/manager/NotificationManager.kt | 19 ++++++-- .../com/xiaoqu/watch/ui/home/HomeFragment.kt | 46 ++++++++++--------- .../xiaoqu/watch/ui/widget/StatusBarView.kt | 36 ++++++--------- 5 files changed, 81 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt b/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt index 5097489..428a5f3 100644 --- a/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt +++ b/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt @@ -7,9 +7,16 @@ import android.view.View import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity 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.ui.widget.NotificationBannerView 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 javax.inject.Inject import kotlin.math.abs @@ -25,8 +32,10 @@ class MainActivity : AppCompatActivity() { /** 系统状态监听器(电量、蓝牙状态) */ @Inject lateinit var systemStateMonitor: SystemStateMonitor - /** 通知横幅(HomeFragment 需访问) */ + @Inject lateinit var notificationManager: NotificationManager + @Inject lateinit var eventBus: EventBus lateinit var notificationBanner: NotificationBannerView + private val activityScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -53,6 +62,9 @@ class MainActivity : AppCompatActivity() { // 初始化通知横幅 notificationBanner = binding.notificationBanner + // 监听 MQTT type=1 → 处理通知 + 显示横幅 + observeMqttMessages() + Timber.d("MainActivity created") } @@ -62,11 +74,21 @@ class MainActivity : AppCompatActivity() { notificationBanner.destroy() } - // ===== 通知横幅(由 HomeFragment 调用显示) ===== + // ===== MQTT 新任务处理 ===== - /** 显示通知横幅(供 HomeFragment 调用) */ - fun showNotificationBanner(count: Int) { - notificationBanner.show(count) + /** 监听 MQTT type=1 → 通知管理器处理 → 横幅 + NewTaskArrived 事件 */ + private fun observeMqttMessages() { + activityScope.launch { + eventBus.events.collect { event -> + if (event is AppEvent.MqttMessageReceived && event.type == 1) { + notificationManager.onNewTaskMessage(event.rawJson) + val count = notificationManager.pendingCount + if (count > 0) { + notificationBanner.show(count) + } + } + } + } } // ===== 下拉手势检测(Activity 级别,不可能被子 View 拦截) ===== diff --git a/app/src/main/java/com/xiaoqu/watch/event/EventBus.kt b/app/src/main/java/com/xiaoqu/watch/event/EventBus.kt index b7cb16b..bdd0f55 100644 --- a/app/src/main/java/com/xiaoqu/watch/event/EventBus.kt +++ b/app/src/main/java/com/xiaoqu/watch/event/EventBus.kt @@ -13,7 +13,7 @@ import javax.inject.Singleton @Singleton class EventBus @Inject constructor() { - private val _events = MutableSharedFlow(replay = 0) + private val _events = MutableSharedFlow(replay = 0, extraBufferCapacity = 64) val events: SharedFlow = _events.asSharedFlow() suspend fun emit(event: AppEvent) { diff --git a/app/src/main/java/com/xiaoqu/watch/service/manager/NotificationManager.kt b/app/src/main/java/com/xiaoqu/watch/service/manager/NotificationManager.kt index ffbfdda..c069f9f 100644 --- a/app/src/main/java/com/xiaoqu/watch/service/manager/NotificationManager.kt +++ b/app/src/main/java/com/xiaoqu/watch/service/manager/NotificationManager.kt @@ -25,7 +25,8 @@ import javax.inject.Singleton @Singleton class NotificationManager @Inject constructor( private val vibrationController: VibrationController, - private val screenController: ScreenController + private val screenController: ScreenController, + private val eventBus: com.xiaoqu.watch.event.EventBus ) { companion object { /** 去抖间隔(毫秒) */ @@ -104,8 +105,12 @@ class NotificationManager @Inject constructor( for (json in jsons) { processMessageSilent(json) // 只加 ID,不重复震动 } - // 合并后通知 UI 更新数字 - onPendingCountChanged?.invoke(pendingCount) + // 合并后通知 UI + scope.launch { + eventBus.emit(com.xiaoqu.watch.event.AppEvent.NewTaskArrived( + _pendingTaskIds.toList(), _pendingTaskIds.size + )) + } } } } @@ -128,7 +133,13 @@ class NotificationManager @Inject constructor( vibrationController.executePattern(pattern) } screenController.turnOn() - // UI 更新(横幅+红点)由调用方处理,不再通过 EventBus + + // 通知 HomeFragment 更新红点 + scope.launch { + eventBus.emit(com.xiaoqu.watch.event.AppEvent.NewTaskArrived( + _pendingTaskIds.toList(), _pendingTaskIds.size + )) + } } /** 静默处理(只加 ID,不震动不亮屏,用于合并暂存消息) */ diff --git a/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt b/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt index a19b025..36c6186 100644 --- a/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt @@ -122,6 +122,9 @@ class HomeFragment : BaseFragment() { // 启动时钟定时器 startClockUpdater() + // 主动获取当前电量(ACTION_BATTERY_CHANGED 是粘性广播,但 EventBus 可能丢失首次事件) + initBatteryStatus() + // 加载任务统计数据 fetchStatistics() @@ -131,11 +134,6 @@ class HomeFragment : BaseFragment() { // 监听打卡状态 observePunchState() - // 去抖合并后更新横幅数字 - notificationManager.onPendingCountChanged = { count -> - (activity as? com.xiaoqu.watch.app.MainActivity)?.showNotificationBanner(count) - fetchStatistics(checkDots = true) - } } override fun onResume() { @@ -187,7 +185,6 @@ class HomeFragment : BaseFragment() { it.onBackKeyPressed = null it.notificationBanner.onClick = null } - notificationManager.onPendingCountChanged = null } // ===== 打卡面板 ===== @@ -338,6 +335,22 @@ class HomeFragment : BaseFragment() { } } + /** 主动获取当前电量(不依赖 EventBus 广播) */ + private fun initBatteryStatus() { + val batteryIntent = requireContext().registerReceiver( + null, android.content.IntentFilter(android.content.Intent.ACTION_BATTERY_CHANGED) + ) + if (batteryIntent != null) { + val level = batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, 0) + val scale = batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_SCALE, 100) + val percent = (level * 100) / scale + val status = batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_STATUS, -1) + val isCharging = status == android.os.BatteryManager.BATTERY_STATUS_CHARGING + || status == android.os.BatteryManager.BATTERY_STATUS_FULL + statusBar.updateBattery(percent, isCharging) + } + } + /** 初始化主页数据 */ private fun initMainPage() { updateClock() @@ -491,28 +504,19 @@ class HomeFragment : BaseFragment() { is AppEvent.BluetoothStateChanged -> { statusBar.updateBluetooth(event.isOn) } + // 新任务到达(由 MainActivity→NotificationManager 处理后发出) + is AppEvent.NewTaskArrived -> { + Timber.d("首页: 新任务到达 (${event.count} 条)") + fetchStatistics(checkDots = true) + setupBannerClick() + } // MQTT 消息 is AppEvent.MqttMessageReceived -> { when (event.type) { 0 -> { - // 日常动态 → 刷新统计 Timber.d("首页: 收到日常动态") 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 -> { // 解绑 → 停止蓝牙 → 清除数据 → 跳绑定页 Timber.d("首页: 收到解绑消息") diff --git a/app/src/main/java/com/xiaoqu/watch/ui/widget/StatusBarView.kt b/app/src/main/java/com/xiaoqu/watch/ui/widget/StatusBarView.kt index 89868f2..b9136ae 100644 --- a/app/src/main/java/com/xiaoqu/watch/ui/widget/StatusBarView.kt +++ b/app/src/main/java/com/xiaoqu/watch/ui/widget/StatusBarView.kt @@ -24,7 +24,7 @@ class StatusBarView @JvmOverloads constructor( // ===== 状态数据 ===== private var bluetoothOn = true private var signalLevel = 4 // 0-4 - private var batteryLevel = 75 // 0-100 + private var batteryLevel = -1 // -1=未获取,0-100=实际电量 private var isCharging = false // ===== 画笔 ===== @@ -47,9 +47,10 @@ class StatusBarView @JvmOverloads constructor( drawBluetoothDot(canvas, 8f, centerY) drawSignalBars(canvas, 22f, centerY) - // ===== 右侧:电量数值 + 电池图标 ===== - val batteryTextWidth = drawBatteryText(canvas, width.toFloat(), centerY) - drawBattery(canvas, width - batteryTextWidth - 52f, centerY) + // ===== 右侧:电池图标(固定位置)+ 电量百分比(电池左侧) ===== + val batteryStartX = width - 48f + drawBattery(canvas, batteryStartX, centerY) + drawBatteryText(canvas, batteryStartX - 4f, centerY) } /** 绘制蓝牙状态圆点 */ @@ -78,19 +79,18 @@ class StatusBarView @JvmOverloads constructor( } } - /** 绘制电量百分比文字(右对齐,返回文字宽度) */ - private fun drawBatteryText(canvas: Canvas, rightEdge: Float, centerY: Float): Float { + /** 绘制电量百分比文字(电池图标左侧) */ + private fun drawBatteryText(canvas: Canvas, rightEdge: Float, centerY: Float) { + if (batteryLevel < 0) return // 未获取时不显示 val text = "${batteryLevel}%" paint.style = Paint.Style.FILL paint.color = 0x99FFFFFF.toInt() // 白色 60% 透明度 - paint.textSize = 16f + paint.textSize = 15f paint.textAlign = Paint.Align.RIGHT - // 垂直居中:baseLine = centerY + textHeight/2 - descent val metrics = paint.fontMetrics val baselineY = centerY - (metrics.ascent + metrics.descent) / 2 - canvas.drawText(text, rightEdge - 2f, baselineY, paint) - paint.textAlign = Paint.Align.LEFT // 恢复默认 - return paint.measureText(text) + 4f + canvas.drawText(text, rightEdge, baselineY, paint) + paint.textAlign = Paint.Align.LEFT } /** 绘制电池图标(壳 + 填充条 + 凸起 + 充电闪电) */ @@ -117,7 +117,7 @@ class StatusBarView @JvmOverloads constructor( } val fillPadding = 2.5f val fillMaxW = shellW - fillPadding * 2 - val fillW = fillMaxW * batteryLevel / 100f + val fillW = fillMaxW * batteryLevel.coerceAtLeast(0) / 100f val fillRect = RectF( startX + fillPadding, shellTop + fillPadding, @@ -126,17 +126,7 @@ class StatusBarView @JvmOverloads constructor( ) canvas.drawRoundRect(fillRect, 2f, 2f, paint) - // 充电闪电标识(电池壳内居中) - if (isCharging) { - paint.color = colorWhite - paint.style = Paint.Style.FILL - paint.textSize = 11f - paint.textAlign = Paint.Align.CENTER - val metrics = paint.fontMetrics - val baselineY = centerY - (metrics.ascent + metrics.descent) / 2 - canvas.drawText("⚡", startX + shellW / 2, baselineY, paint) - paint.textAlign = Paint.Align.LEFT - } + // 充电状态通过填充条颜色区分(绿色=充电中,已在上方 when 处理) // 电池凸起(右侧小矩形) paint.color = colorBorder