fix: 息屏后MQTT断连导致无法收到通知

添加前台服务+WakeLock保活MQTT连接,请求电池优化白名单绕过Doze限制,
AlarmManager每5分钟健康检查断连自动重连。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-05-09 13:10:04 +09:30
parent 966c9f9c0d
commit b867c33015
6 changed files with 301 additions and 5 deletions

View File

@@ -0,0 +1,85 @@
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)
}
}