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:
@@ -27,11 +27,6 @@
|
|||||||
<!-- WakeLock -->
|
<!-- WakeLock -->
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<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) -->
|
<!-- 手机状态(获取 IMEI) -->
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
@@ -79,11 +74,6 @@
|
|||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<!-- MQTT 前台服务(息屏保活) -->
|
|
||||||
<service
|
|
||||||
android:name=".service.MqttService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<!-- 开机自启广播接收器 -->
|
<!-- 开机自启广播接收器 -->
|
||||||
<receiver
|
<receiver
|
||||||
@@ -95,11 +85,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<!-- MQTT 连接健康检查(定时闹钟触发) -->
|
|
||||||
<receiver
|
|
||||||
android:name=".service.MqttAlarmReceiver"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -3,27 +3,17 @@ package com.xiaoqu.watch.app
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.xiaoqu.watch.service.MqttAlarmReceiver
|
|
||||||
import com.xiaoqu.watch.service.MqttService
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开机自启广播接收器
|
* 开机自启广播接收器
|
||||||
* 收到 BOOT_COMPLETED 后启动 MQTT 服务 + MainActivity
|
* 收到 BOOT_COMPLETED 后启动 MainActivity(MQTT 在 SplashFragment 中连接)
|
||||||
*/
|
*/
|
||||||
class BootReceiver : BroadcastReceiver() {
|
class BootReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
|
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
|
||||||
Timber.d("Boot completed, starting MqttService + MainActivity")
|
Timber.d("Boot completed, launching MainActivity")
|
||||||
|
|
||||||
// 先启动 MQTT 前台服务(尽早恢复连接)
|
|
||||||
MqttService.start(context)
|
|
||||||
|
|
||||||
// 启动连接健康检查定时器
|
|
||||||
MqttAlarmReceiver.schedule(context)
|
|
||||||
|
|
||||||
// 启动主界面
|
|
||||||
val launchIntent = Intent(context, MainActivity::class.java).apply {
|
val launchIntent = Intent(context, MainActivity::class.java).apply {
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
package com.xiaoqu.watch.app
|
package com.xiaoqu.watch.app
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.PowerManager
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
@@ -98,9 +93,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
// 监听 MQTT type=1 → 处理通知 + 显示横幅
|
// 监听 MQTT type=1 → 处理通知 + 显示横幅
|
||||||
observeMqttMessages()
|
observeMqttMessages()
|
||||||
|
|
||||||
// 请求电池优化白名单(绕过 Doze 模式限制)
|
|
||||||
requestBatteryWhitelist()
|
|
||||||
|
|
||||||
Timber.d("MainActivity created")
|
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 更新 =====
|
// ===== OTA 更新 =====
|
||||||
|
|
||||||
/** 设置更新弹窗按钮回调 */
|
/** 设置更新弹窗按钮回调 */
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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())
|
|
||||||
|
|
||||||
// 连接 MQTT(Paho 库内部会管理自己的心跳和重连)
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,6 @@ import com.xiaoqu.watch.data.prefs.UserPrefs
|
|||||||
import com.xiaoqu.watch.network.ApiResult
|
import com.xiaoqu.watch.network.ApiResult
|
||||||
import com.xiaoqu.watch.network.api.CommonApi
|
import com.xiaoqu.watch.network.api.CommonApi
|
||||||
import com.xiaoqu.watch.network.safeApiCall
|
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.service.manager.MqttManager
|
||||||
import com.xiaoqu.watch.util.DeviceUtil
|
import com.xiaoqu.watch.util.DeviceUtil
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@@ -49,13 +47,10 @@ class SplashFragment : Fragment() {
|
|||||||
// 1. 初始化设备信息(首次启动时写入 SP)
|
// 1. 初始化设备信息(首次启动时写入 SP)
|
||||||
initDevicePrefs()
|
initDevicePrefs()
|
||||||
|
|
||||||
// 2. 启动 MQTT 前台服务(保持息屏后连接存活)
|
// 2. 连接 MQTT(定制 ROM + Launcher 应用,系统不会杀进程)
|
||||||
MqttService.start(requireContext())
|
mqttManager.connect()
|
||||||
|
|
||||||
// 3. 启动连接健康检查定时器(Doze 模式下定期检查)
|
// 3. 检查绑定状态并导航
|
||||||
MqttAlarmReceiver.schedule(requireContext())
|
|
||||||
|
|
||||||
// 4. 检查绑定状态并导航
|
|
||||||
checkBindStatus()
|
checkBindStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user