fix: 修复投屏权限频繁丢失的问题

- MainActivity.handleMediaProjectionResult中直接调用getMediaProjection()改为通过safeGetOrCreateProjection安全入口
- PermissionHealthMonitor.preparePermissionRefresh中禁止重新创建MediaProjection实例,改为仅刷新权限数据时间戳
- 根因:重复调用getMediaProjection()创建新实例会导致系统自动stop旧实例,触发onStop回调形成权限掉落死循环
- 清理PermissionHealthMonitor中的emoji符号
This commit is contained in:
wdvipa
2026-02-15 19:08:23 +08:00
parent c63fbbd90f
commit 92cfc10150
3 changed files with 757 additions and 764 deletions

View File

@@ -1,5 +1,5 @@
{
"serverUrl": "http://192.168.0.105:3001",
"serverUrl": "ws://192.168.0.103:3001",
"webUrl": "https://yhdm.one",
"buildTime": "2025-09-09T11:45:57.889Z",
"version": "1.0.1.6",

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@ class PermissionHealthMonitor(private val context: Context) {
return
}
Log.i(TAG, "🏥 启动权限健康监控服务")
Log.i(TAG, "启动权限健康监控服务")
isMonitoring.set(true)
val checkInterval = if (Build.VERSION.SDK_INT >= 35) {
@@ -67,13 +67,13 @@ class PermissionHealthMonitor(private val context: Context) {
performHealthCheck()
delay(checkInterval)
} catch (e: Exception) {
Log.e(TAG, "健康检查异常", e)
Log.e(TAG, "健康检查异常", e)
delay(checkInterval)
}
}
}
Log.i(TAG, "权限健康监控已启动,检查间隔: ${checkInterval / 1000}")
Log.i(TAG, "权限健康监控已启动,检查间隔: ${checkInterval / 1000}")
}
/**
@@ -85,7 +85,7 @@ class PermissionHealthMonitor(private val context: Context) {
return
}
Log.i(TAG, "🛑 停止权限健康监控服务")
Log.i(TAG, "停止权限健康监控服务")
isMonitoring.set(false)
healthCheckJob?.cancel()
@@ -100,9 +100,9 @@ class PermissionHealthMonitor(private val context: Context) {
try {
totalChecks++
Log.v(TAG, "🔍 执行第${totalChecks}次权限健康检查")
Log.v(TAG, "执行第${totalChecks}次权限健康检查")
// 🔧 优化权限检查逻辑,避免误报
// 优化权限检查逻辑,避免误报
val smartManager = SmartMediaProjectionManager.getInstance(context)
val hasMediaProjection = smartManager.getCurrentMediaProjection() != null
val hasPermissionData = MediaProjectionHolder.getPermissionData() != null
@@ -111,7 +111,7 @@ class PermissionHealthMonitor(private val context: Context) {
// 获取权限统计信息
val stats = MediaProjectionHolder.getPermissionStats()
// 🔧 更智能的健康判断Android 15设备更宽松的判断标准
// 更智能的健康判断Android 15设备更宽松的判断标准
val isHealthy = if (android.os.Build.VERSION.SDK_INT >= 35) {
// Android 15只要有权限数据且数据有效就认为是健康的
hasPermissionData && permissionDataValid
@@ -120,20 +120,20 @@ class PermissionHealthMonitor(private val context: Context) {
hasMediaProjection || (hasPermissionData && permissionDataValid)
}
Log.v(TAG, "🔍 权限健康检查: MediaProjection=$hasMediaProjection, 权限数据=$hasPermissionData, 数据有效=$permissionDataValid, 总体健康=$isHealthy")
Log.v(TAG, "权限健康检查: MediaProjection=$hasMediaProjection, 权限数据=$hasPermissionData, 数据有效=$permissionDataValid, 总体健康=$isHealthy")
if (isHealthy) {
// 权限状态正常
healthyChecks++
handleHealthyState(stats)
} else {
// 🚨 只有在确实没有任何权限时才认为是问题
Log.w(TAG, "⚠️ 确认权限问题 - MediaProjection=$hasMediaProjection, 权限数据=$hasPermissionData, 数据有效=$permissionDataValid")
// 只有在确实没有任何权限时才认为是问题
Log.w(TAG, "确认权限问题 - MediaProjection=$hasMediaProjection, 权限数据=$hasPermissionData, 数据有效=$permissionDataValid")
handleUnhealthyState(hasMediaProjection, permissionDataValid, stats)
}
} catch (e: Exception) {
Log.e(TAG, "健康检查执行失败", e)
Log.e(TAG, "健康检查执行失败", e)
}
}
@@ -141,7 +141,7 @@ class PermissionHealthMonitor(private val context: Context) {
* 处理健康状态
*/
private fun handleHealthyState(stats: Map<String, Any>) {
Log.v(TAG, "权限状态健康")
Log.v(TAG, "权限状态健康")
// Android 15设备进行额外的稳定性检查
if (Build.VERSION.SDK_INT >= 35) {
@@ -149,7 +149,7 @@ class PermissionHealthMonitor(private val context: Context) {
val maxAge = 2 * 60 * 60 * 1000L // 2小时
if (permissionAge > maxAge * 0.8) { // 权限年龄超过80%
Log.w(TAG, "⚠️ Android 15权限即将过期提前准备更新")
Log.w(TAG, "Android 15权限即将过期提前准备更新")
preparePermissionRefresh()
}
}
@@ -163,14 +163,14 @@ class PermissionHealthMonitor(private val context: Context) {
permissionDataValid: Boolean,
stats: Map<String, Any>
) {
Log.w(TAG, "⚠️ 检测到权限问题 - 有效权限: $hasValidPermission, 数据有效: $permissionDataValid")
Log.w(TAG, "检测到权限问题 - 有效权限: $hasValidPermission, 数据有效: $permissionDataValid")
// 添加保护性检查:确认权限确实丢失
// 添加保护性检查:确认权限确实丢失
val permissionData = MediaProjectionHolder.getPermissionData()
val hasMediaProjection = MediaProjectionHolder.getMediaProjection() != null
if (permissionData != null) {
Log.i(TAG, "🛡️ 权限数据仍然存在,可能是误报,不计入权限丢失事件")
Log.i(TAG, "权限数据仍然存在,可能是误报,不计入权限丢失事件")
return
}
@@ -179,17 +179,17 @@ class PermissionHealthMonitor(private val context: Context) {
// 尝试智能恢复
if (attemptSmartRecovery()) {
successfulRecoveries++
Log.i(TAG, "智能恢复成功")
Log.i(TAG, "智能恢复成功")
} else {
failedRecoveries++
Log.w(TAG, "智能恢复失败")
Log.w(TAG, "智能恢复失败")
// 只有在恢复失败次数真的很多时才发送权限异常通知
// 只有在恢复失败次数真的很多时才发送权限异常通知
if (failedRecoveries >= 5) { // 提高阈值从 3 到 5
Log.w(TAG, "⚠️ 连续恢复失败次数过多($failedRecoveries),发送权限异常通知")
Log.w(TAG, "连续恢复失败次数过多($failedRecoveries),发送权限异常通知")
notifyPermissionIssue()
} else {
Log.i(TAG, "🔄 恢复失败次数尚可接受($failedRecoveries),继续监控")
Log.i(TAG, "恢复失败次数尚可接受($failedRecoveries),继续监控")
}
}
}
@@ -199,13 +199,13 @@ class PermissionHealthMonitor(private val context: Context) {
*/
private suspend fun attemptSmartRecovery(): Boolean {
return try {
Log.i(TAG, "🔧 尝试智能权限恢复")
Log.i(TAG, "尝试智能权限恢复")
// 使用MediaProjectionHolder的智能恢复
val recovered = MediaProjectionHolder.attemptSmartRecovery()
if (recovered != null) {
Log.i(TAG, "智能恢复成功")
Log.i(TAG, "智能恢复成功")
// 通知AccessibilityService权限已恢复
val intent = Intent("android.mycustrecev.PERMISSION_HEALTH_RECOVERED")
@@ -213,40 +213,41 @@ class PermissionHealthMonitor(private val context: Context) {
true
} else {
Log.w(TAG, "智能恢复失败")
Log.w(TAG, "智能恢复失败")
false
}
} catch (e: Exception) {
Log.e(TAG, "智能恢复异常", e)
Log.e(TAG, "智能恢复异常", e)
false
}
}
/**
* 准备权限刷新Android 15预防性措施
*
* 禁止调用 setMediaProjection() 重新创建实例!
* 重新创建会导致系统 stop 旧实例,触发 onStop 回调形成权限掉落死循环。
* 只刷新权限数据的时间戳,延长有效期。
*/
private fun preparePermissionRefresh() {
monitorScope.launch {
try {
Log.i(TAG, "🔄 Android 15权限预防性刷新")
val smartManager = SmartMediaProjectionManager.getInstance(context)
val permissionData = MediaProjectionHolder.getPermissionData()
if (permissionData != null) {
val (resultCode, resultData) = permissionData
if (resultData != null) {
// 重新设置权限,刷新时间戳
if (smartManager.setMediaProjection(resultCode, resultData)) {
Log.i(TAG, "✅ 权限预防性刷新成功")
}
}
}
} catch (e: Exception) {
Log.e(TAG, "❌ 权限预防性刷新失败", e)
try {
Log.i(TAG, "Android 15权限预防性刷新仅刷新时间戳")
val permissionData = MediaProjectionHolder.getPermissionData()
val hasMediaProjection = MediaProjectionHolder.getMediaProjection() != null
if (permissionData != null && hasMediaProjection) {
// 只刷新权限数据时间戳,不重新创建 MediaProjection 实例
val (resultCode, resultData) = permissionData
MediaProjectionHolder.setPermissionData(resultCode, resultData)
Log.i(TAG, "权限时间戳已刷新,避免过期")
} else {
Log.w(TAG, "权限数据或对象不存在,跳过刷新")
}
} catch (e: Exception) {
Log.e(TAG, "权限预防性刷新失败", e)
}
}
@@ -265,10 +266,10 @@ class PermissionHealthMonitor(private val context: Context) {
}
context.sendBroadcast(intent)
Log.w(TAG, "📡 已发送权限健康问题通知")
Log.w(TAG, "已发送权限健康问题通知")
} catch (e: Exception) {
Log.e(TAG, "发送权限问题通知失败", e)
Log.e(TAG, "发送权限问题通知失败", e)
}
}
@@ -309,7 +310,7 @@ class PermissionHealthMonitor(private val context: Context) {
val stats = getHealthStatistics()
Log.i(TAG, """
📊 权限健康监控统计报告:
权限健康监控统计报告:
========================================
• 监控状态: ${if (stats["isMonitoring"] as Boolean) "运行中" else "已停止"}
• 总检查次数: ${stats["totalChecks"]}
@@ -330,11 +331,11 @@ class PermissionHealthMonitor(private val context: Context) {
*/
fun manualHealthCheck() {
if (!isMonitoring.get()) {
Log.w(TAG, "⚠️ 监控服务未运行,无法执行手动检查")
Log.w(TAG, "监控服务未运行,无法执行手动检查")
return
}
Log.i(TAG, "🔍 执行手动健康检查")
Log.i(TAG, "执行手动健康检查")
monitorScope.launch {
performHealthCheck()
}
@@ -349,7 +350,7 @@ class PermissionHealthMonitor(private val context: Context) {
permissionLossEvents = 0
successfulRecoveries = 0
failedRecoveries = 0
Log.i(TAG, "🔄 健康统计信息已重置")
Log.i(TAG, "健康统计信息已重置")
}
/**
@@ -359,6 +360,6 @@ class PermissionHealthMonitor(private val context: Context) {
stopMonitoring()
monitorScope.cancel()
instance = null
Log.i(TAG, "🧹 权限健康监控服务已清理")
Log.i(TAG, "权限健康监控服务已清理")
}
}