Files
xqwatch/app/src/main/java/com/xiaoqu/watch/service/MqttAlarmReceiver.kt
dongliang b867c33015 fix: 息屏后MQTT断连导致无法收到通知
添加前台服务+WakeLock保活MQTT连接,请求电池优化白名单绕过Doze限制,
AlarmManager每5分钟健康检查断连自动重连。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-09 13:10:04 +09:30

86 lines
3.1 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.xiaoqu.watch.service
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.SystemClock
import com.xiaoqu.watch.service.manager.MqttManager
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject
/**
* MQTT 连接健康检查广播接收器
*
* 通过 AlarmManager.setExactAndAllowWhileIdle() 定期触发,
* 在 Doze 模式的维护窗口中检查 MQTT 连接状态,断连则重连。
* 同时确保 MqttService 前台服务存活。
*/
@AndroidEntryPoint
class MqttAlarmReceiver : BroadcastReceiver() {
companion object {
/** 检查间隔5 分钟Doze 维护窗口至少 10 分钟5 分钟能抓到窗口期) */
private const val CHECK_INTERVAL_MS = 5 * 60 * 1000L
/** 启动定时健康检查 */
fun schedule(context: Context) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, MqttAlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// setExactAndAllowWhileIdle: Doze 模式下也能触发
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + CHECK_INTERVAL_MS,
pendingIntent
)
} else {
alarmManager.setExact(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + CHECK_INTERVAL_MS,
pendingIntent
)
}
Timber.d("MqttAlarm: 已设置 ${CHECK_INTERVAL_MS / 1000}s 后检查")
}
/** 取消定时检查 */
fun cancel(context: Context) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, MqttAlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
alarmManager.cancel(pendingIntent)
Timber.d("MqttAlarm: 已取消定时检查")
}
}
@Inject lateinit var mqttManager: MqttManager
override fun onReceive(context: Context, intent: Intent) {
Timber.d("MqttAlarm: 健康检查触发, isConnected=${mqttManager.isConnected}")
// 确保前台服务在运行
MqttService.start(context)
// MQTT 断连则重连
if (!mqttManager.isConnected) {
Timber.w("MqttAlarm: 连接已断开,触发重连")
mqttManager.connect()
}
// 重新设置下一次闹钟setExactAndAllowWhileIdle 是一次性的)
schedule(context)
}
}