fix: 移除前台服务/电池白名单/AlarmManager,恢复旧版MQTT直连方案

定制ROM+Launcher应用不会被系统杀死,保活机制是多余的耗电源头。
回到和旧版一样的方案:SplashFragment直连MQTT + Paho自动重连。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-05-11 11:40:15 +09:30
parent 1214ca92a4
commit 20a0389b07
6 changed files with 5 additions and 263 deletions

View File

@@ -27,11 +27,6 @@
<!-- WakeLock -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 前台服务MQTT 保活) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 电池优化白名单(绕过 Doze 限制) -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- 手机状态(获取 IMEI -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
@@ -79,11 +74,6 @@
android:resource="@xml/file_paths" />
</provider>
<!-- MQTT 前台服务(息屏保活) -->
<service
android:name=".service.MqttService"
android:enabled="true"
android:exported="false" />
<!-- 开机自启广播接收器 -->
<receiver
@@ -95,11 +85,6 @@
</intent-filter>
</receiver>
<!-- MQTT 连接健康检查(定时闹钟触发) -->
<receiver
android:name=".service.MqttAlarmReceiver"
android:enabled="true"
android:exported="false" />
</application>

View File

@@ -3,27 +3,17 @@ package com.xiaoqu.watch.app
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.xiaoqu.watch.service.MqttAlarmReceiver
import com.xiaoqu.watch.service.MqttService
import timber.log.Timber
/**
* 开机自启广播接收器
* 收到 BOOT_COMPLETED 后启动 MQTT 服务 + MainActivity
* 收到 BOOT_COMPLETED 后启动 MainActivityMQTT 在 SplashFragment 中连接)
*/
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
Timber.d("Boot completed, starting MqttService + MainActivity")
// 先启动 MQTT 前台服务(尽早恢复连接)
MqttService.start(context)
// 启动连接健康检查定时器
MqttAlarmReceiver.schedule(context)
// 启动主界面
Timber.d("Boot completed, launching MainActivity")
val launchIntent = Intent(context, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}

View File

@@ -1,12 +1,7 @@
package com.xiaoqu.watch.app
import android.content.Intent
import android.content.pm.ActivityInfo
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.provider.Settings
import android.view.MotionEvent
import android.view.View
import androidx.navigation.findNavController
@@ -98,9 +93,6 @@ class MainActivity : AppCompatActivity() {
// 监听 MQTT type=1 → 处理通知 + 显示横幅
observeMqttMessages()
// 请求电池优化白名单(绕过 Doze 模式限制)
requestBatteryWhitelist()
Timber.d("MainActivity created")
}
@@ -235,31 +227,6 @@ class MainActivity : AppCompatActivity() {
}
}
// ===== 电池优化白名单 =====
/**
* 请求加入电池优化白名单Doze 模式下允许后台网络访问)
* 已在白名单中则跳过,避免重复弹窗
*/
private fun requestBatteryWhitelist() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = getSystemService(PowerManager::class.java)
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
try {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
Timber.d("电池白名单: 已请求")
} catch (e: Exception) {
Timber.w(e, "电池白名单: 请求失败")
}
} else {
Timber.d("电池白名单: 已在白名单中")
}
}
}
// ===== OTA 更新 =====
/** 设置更新弹窗按钮回调 */

View File

@@ -1,76 +0,0 @@
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.SystemClock
import com.xiaoqu.watch.service.manager.MqttManager
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject
/**
* MQTT 连接健康检查广播接收器
*
* 通过 AlarmManager 定期触发(不强制唤醒,等系统维护窗口),
* 检查 MQTT 连接状态,断连则重连。
* 同时确保 MqttService 前台服务存活。
*/
@AndroidEntryPoint
class MqttAlarmReceiver : BroadcastReceiver() {
companion object {
/** 检查间隔15 分钟(减少唤醒频率,前台服务+电池白名单已足够保活) */
private const val CHECK_INTERVAL_MS = 15 * 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
)
// 不唤醒设备,等系统下次维护窗口时执行(省电)
alarmManager.set(
AlarmManager.ELAPSED_REALTIME, // 不强制唤醒
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)
}
}

View File

@@ -1,119 +0,0 @@
package com.xiaoqu.watch.service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import com.xiaoqu.watch.R
import com.xiaoqu.watch.service.manager.MqttManager
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject
/**
* MQTT 前台服务
*
* 保持 MQTT 连接在息屏/Doze 模式下不被系统杀死。
* 通过 startForegroundService() 启动,显示常驻通知。
* 不持有永久 WakeLock省电依赖
* - 前台服务:防止进程被杀
* - 电池白名单:绕过 Doze 网络限制
* - Paho 库内置心跳机制:自动维护连接
* - AlarmManager 健康检查:兜底重连
*/
@AndroidEntryPoint
class MqttService : Service() {
companion object {
/** 通知渠道 ID */
private const val CHANNEL_ID = "mqtt_service"
/** 前台通知 ID */
private const val NOTIFICATION_ID = 1
/** 启动服务 */
fun start(context: Context) {
val intent = Intent(context, MqttService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
/** 停止服务 */
fun stop(context: Context) {
context.stopService(Intent(context, MqttService::class.java))
}
}
@Inject lateinit var mqttManager: MqttManager
override fun onCreate() {
super.onCreate()
Timber.d("MqttService: onCreate")
// 创建通知渠道
createNotificationChannel()
// 启动前台服务(必须在 5 秒内调用,否则 ANR
startForeground(NOTIFICATION_ID, buildNotification())
// 连接 MQTTPaho 库内部会管理自己的心跳和重连)
mqttManager.connect()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.d("MqttService: onStartCommand")
// 如果连接断了,重新连接
if (!mqttManager.isConnected) {
mqttManager.connect()
}
// 被杀后自动重启
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
Timber.d("MqttService: onDestroy")
mqttManager.disconnect()
}
override fun onBind(intent: Intent?): IBinder? = null
/** 创建通知渠道Android 8.0+ */
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"消息连接",
NotificationManager.IMPORTANCE_LOW // 低优先级,不发声不振动
).apply {
description = "保持消息连接在线"
setShowBadge(false)
}
val nm = getSystemService(NotificationManager::class.java)
nm.createNotificationChannel(channel)
}
}
/** 构建前台通知(常驻,低优先级) */
private fun buildNotification(): Notification {
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(this, CHANNEL_ID)
} else {
@Suppress("DEPRECATION")
Notification.Builder(this)
}
return builder
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("小趣智清洁")
.setContentText("消息连接中")
.setOngoing(true)
.build()
}
}

View File

@@ -13,8 +13,6 @@ import com.xiaoqu.watch.data.prefs.UserPrefs
import com.xiaoqu.watch.network.ApiResult
import com.xiaoqu.watch.network.api.CommonApi
import com.xiaoqu.watch.network.safeApiCall
import com.xiaoqu.watch.service.MqttAlarmReceiver
import com.xiaoqu.watch.service.MqttService
import com.xiaoqu.watch.service.manager.MqttManager
import com.xiaoqu.watch.util.DeviceUtil
import dagger.hilt.android.AndroidEntryPoint
@@ -49,13 +47,10 @@ class SplashFragment : Fragment() {
// 1. 初始化设备信息(首次启动时写入 SP
initDevicePrefs()
// 2. 启动 MQTT 前台服务(保持息屏后连接存活
MqttService.start(requireContext())
// 2. 连接 MQTT(定制 ROM + Launcher 应用,系统不会杀进程
mqttManager.connect()
// 3. 启动连接健康检查定时器Doze 模式下定期检查)
MqttAlarmReceiver.schedule(requireContext())
// 4. 检查绑定状态并导航
// 3. 检查绑定状态并导航
checkBindStatus()
}