You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通话接听页使用PowerManager.WakeLock引发内存泄漏问题排查

问题描述

我在实现通话接听界面时,通过**距离传感器(Proximity Sensor)**和PowerManager.WakeLock实现手机贴近面部时熄屏的功能。功能可以正常工作,但存在内存泄漏问题——即使Fragment代码很简洁时就检测到泄漏,排查后确定根源是PowerManager.WakeLock。我已经在Fragment的onPause、onDestroyView、onDestroy等生命周期节点尝试释放锁,但泄漏依然存在,同时日志中出现WakeLock finalized while still held错误。

内存泄漏分析截图:
内存泄漏分析1
内存泄漏分析2

当前Fragment实现代码:

override val sensorManager: SensorManager
    get() = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager
override var proximitySensor: Sensor? = null
override val powerManager: PowerManager =
    requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
override val lock: PowerManager.WakeLock = powerManager.newWakeLock(
    PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
    PROXIMITY_WAKE_LOG_TAG
)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

    activity?.onBackPressedDispatcher?.addCallback {
        activity?.finishAndRemoveTask()
    }
}

override fun onResume() {
    super.onResume()
    proximitySensor?.let { proximity ->
        sensorManager.apply {
            registerListener(
                this@AnswerFragment,
                proximity,
                SensorManager.SENSOR_DELAY_NORMAL
            )
        }
    }
}

override fun onPause() {
    super.onPause()
    sensorManager.unregisterListener(this)
    if (lock.isHeld) {
        lock.release()
    }
}

override fun onDestroyView() {
    super.onDestroyView()
    proximitySensor = null
    if (lock.isHeld) {
        lock.release()
    }
    _binding = null
}

override fun onDestroy() {
    if (lock.isHeld) {
        lock.release()
    }
    super.onDestroy()
}

override fun onSensorChanged(event: SensorEvent?) {
    if (event?.values?.get(0) == 0.0f) {
        // 贴近面部,熄屏
        lock.acquire()
    } else {
        // 远离面部,亮屏
        if (lock.isHeld) {
            lock.release()
        }
    }
}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
问题根源
  • WakeLock引用计数不匹配:默认情况下WakeLock带引用计数,每次acquire()计数加1,release()计数减1。如果传感器频繁触发贴近动作(比如面部来回移动),会多次调用acquire(),但你释放时仅调用一次release(),最终锁仍处于持有状态,GC回收时就会触发WakeLock finalized while still held错误,导致Fragment被锁引用无法回收,引发内存泄漏。
  • WakeLock实例未及时置空:你将lock声明为Fragment的成员属性并提前初始化,即使释放了锁,只要引用存在,Fragment就无法被彻底回收。
  • 传感器回调逻辑有漏洞:未判断锁是否已持有就直接调用acquire(),导致重复持有锁。
修复方案

调整初始化时机、彻底释放锁并清空引用,修复后的代码如下:

// 改为可空私有变量,避免提前初始化
private var sensorManager: SensorManager? = null
private var proximitySensor: Sensor? = null
private var powerManager: PowerManager? = null
private var lock: PowerManager.WakeLock? = null
private val PROXIMITY_WAKE_LOG_TAG = "ProximityWakeLock"

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    // 延迟初始化服务和锁,与Fragment生命周期绑定
    sensorManager = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager
    powerManager = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
    proximitySensor = sensorManager?.getDefaultSensor(Sensor.TYPE_PROXIMITY)
    
    powerManager?.let { pm ->
        lock = pm.newWakeLock(
            PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
            PROXIMITY_WAKE_LOG_TAG
        )
    }

    activity?.onBackPressedDispatcher?.addCallback {
        activity?.finishAndRemoveTask()
    }
}

override fun onResume() {
    super.onResume()
    proximitySensor?.let { proximity ->
        sensorManager?.registerListener(
            this@AnswerFragment,
            proximity,
            SensorManager.SENSOR_DELAY_NORMAL
        )
    }
}

override fun onPause() {
    super.onPause()
    sensorManager?.unregisterListener(this)
    // 彻底释放锁
    releaseWakeLock()
}

override fun onDestroyView() {
    super.onDestroyView()
    proximitySensor = null
    releaseWakeLock()
    _binding = null
}

override fun onDestroy() {
    releaseWakeLock()
    // 清空所有引用,让GC能正常回收Fragment
    sensorManager = null
    powerManager = null
    lock = null
    super.onDestroy()
}

override fun onSensorChanged(event: SensorEvent?) {
    val currentLock = lock ?: return
    if (event?.values?.get(0) == 0.0f) {
        // 仅当锁未持有时才获取,避免重复计数
        if (!currentLock.isHeld) {
            currentLock.acquire()
        }
    } else {
        // 远离时直接彻底释放锁
        releaseWakeLock()
    }
}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}

/**
 * 循环释放直到锁不再持有,处理多次acquire的情况
 */
private fun releaseWakeLock() {
    lock?.let { wakeLock ->
        while (wakeLock.isHeld) {
            wakeLock.release()
        }
    }
}
额外提示
  • PROXIMITY_SCREEN_OFF_WAKE_LOCK是系统特殊锁,理论上传感器检测到远离会自动释放,但手动处理更稳妥,可避免异常场景。
  • 修改完成后,建议用LeakCanary等工具再次检测,确认内存泄漏问题已解决。

内容的提问来源于stack exchange,提问作者JV17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 00:10:47