fix: 修复屏幕录制权限频繁掉落的根因问题
- MediaProjectionHolder添加全局创建锁safeGetOrCreateProjection() - 所有创建点统一通过安全入口,禁止直接调用getMediaProjection() - 重复创建新实例会导致系统stop旧实例触发onStop死循环 - Android15MediaProjectionManager/SmartMediaProjectionManager的onStop回调添加isCreating检查 - RemoteControlForegroundService备用方案改用安全创建入口 - AccessibilityRemoteService静默恢复改用安全创建入口 - 添加最小创建间隔5秒防止短时间内重复创建
This commit is contained in:
@@ -5862,12 +5862,28 @@ class MainActivity : AppCompatActivity() {
|
||||
/**
|
||||
* MediaProjection静态持有者 - 增强版
|
||||
* 专门针对Android 15权限丢失问题进行优化
|
||||
*
|
||||
* 🚨 核心修复:添加全局创建锁,确保同一时刻只有一个地方能创建 MediaProjection 实例。
|
||||
* 重复调用 getMediaProjection(resultCode, resultData) 会创建新实例,
|
||||
* 系统会自动 stop 旧实例并触发 onStop 回调,形成权限掉落死循环。
|
||||
*/
|
||||
object MediaProjectionHolder {
|
||||
private var mediaProjection: MediaProjection? = null
|
||||
private var permissionResultCode: Int? = null
|
||||
private var permissionData: Intent? = null
|
||||
|
||||
// 🔒 全局创建锁:防止多个管理器并发调用 getMediaProjection() 创建新实例
|
||||
private val creationLock = java.util.concurrent.locks.ReentrantLock()
|
||||
|
||||
// 🔒 创建中标记:防止 onStop 回调触发的恢复流程与正在进行的创建冲突
|
||||
@Volatile
|
||||
private var isCreatingProjection = false
|
||||
|
||||
// 🔒 上次创建时间:防止短时间内重复创建
|
||||
@Volatile
|
||||
private var lastCreationTime: Long = 0L
|
||||
private const val MIN_CREATION_INTERVAL = 5000L // 最小创建间隔5秒
|
||||
|
||||
// Android 15权限保护增强
|
||||
@Volatile
|
||||
private var permissionCreationTime: Long = 0L
|
||||
@@ -5984,6 +6000,87 @@ object MediaProjectionHolder {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔒 安全创建 MediaProjection 的唯一入口
|
||||
*
|
||||
* 所有需要创建 MediaProjection 的地方都必须通过此方法,
|
||||
* 禁止直接调用 mediaProjectionManager.getMediaProjection()。
|
||||
*
|
||||
* 核心保护机制:
|
||||
* 1. 优先返回已有对象,避免重复创建
|
||||
* 2. 全局创建锁,防止并发创建
|
||||
* 3. 最小创建间隔,防止短时间内重复创建触发 onStop 死循环
|
||||
*/
|
||||
fun safeGetOrCreateProjection(
|
||||
context: android.content.Context,
|
||||
resultCode: Int,
|
||||
resultData: Intent
|
||||
): MediaProjection? {
|
||||
// 第一步:优先返回已有对象
|
||||
val existing = mediaProjection
|
||||
if (existing != null) {
|
||||
Log.i("MediaProjectionHolder", "✅ safeCreate: 已有有效对象,直接复用")
|
||||
return existing
|
||||
}
|
||||
|
||||
// 第二步:检查创建间隔
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - lastCreationTime < MIN_CREATION_INTERVAL) {
|
||||
Log.w("MediaProjectionHolder", "⚠️ safeCreate: 距上次创建不足${MIN_CREATION_INTERVAL}ms,跳过")
|
||||
return null
|
||||
}
|
||||
|
||||
// 第三步:加锁创建,防止并发
|
||||
if (!creationLock.tryLock()) {
|
||||
Log.w("MediaProjectionHolder", "⚠️ safeCreate: 其他线程正在创建,跳过")
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
// double-check:锁内再次检查
|
||||
val doubleCheck = mediaProjection
|
||||
if (doubleCheck != null) {
|
||||
Log.i("MediaProjectionHolder", "✅ safeCreate: double-check发现已有对象")
|
||||
return doubleCheck
|
||||
}
|
||||
|
||||
isCreatingProjection = true
|
||||
Log.i("MediaProjectionHolder", "🔒 safeCreate: 开始创建新的MediaProjection")
|
||||
|
||||
val manager = context.getSystemService(
|
||||
android.content.Context.MEDIA_PROJECTION_SERVICE
|
||||
) as? android.media.projection.MediaProjectionManager
|
||||
|
||||
if (manager == null) {
|
||||
Log.e("MediaProjectionHolder", "❌ safeCreate: MediaProjectionManager为null")
|
||||
return null
|
||||
}
|
||||
|
||||
val projection = manager.getMediaProjection(resultCode, resultData)
|
||||
if (projection != null) {
|
||||
mediaProjection = projection
|
||||
lastCreationTime = now
|
||||
permissionCreationTime = now
|
||||
Log.i("MediaProjectionHolder", "✅ safeCreate: 创建成功 hash=${projection.hashCode()}")
|
||||
} else {
|
||||
Log.w("MediaProjectionHolder", "❌ safeCreate: getMediaProjection返回null")
|
||||
}
|
||||
|
||||
return projection
|
||||
} catch (e: Exception) {
|
||||
Log.e("MediaProjectionHolder", "❌ safeCreate: 创建异常", e)
|
||||
return null
|
||||
} finally {
|
||||
isCreatingProjection = false
|
||||
creationLock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否正在创建 MediaProjection(供 onStop 回调判断是否应跳过恢复)
|
||||
*/
|
||||
fun isCreating(): Boolean = isCreatingProjection
|
||||
|
||||
// 新增:强制清理方法,仅在确实需要停止时使用
|
||||
fun forceStopMediaProjection() {
|
||||
Log.i("MediaProjectionHolder", "🛑 强制停止MediaProjection(仅在用户主动停止时使用)")
|
||||
|
||||
Reference in New Issue
Block a user