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

SensorManager unregisterListener失效:通话结束后无法停止距离传感器

问题

我希望在接听电话时读取距离传感器的数据:处于OFFHOOK状态时可正常读取,但通话结束进入IDLE状态时,调用proximitySensorListener.stopSensors()无法停止传感器。若在通话中启动传感器监听后通过延迟调用stopSensors可停止传感器,但这并非理想方案,请问为何直接调用无法生效?

相关代码如下:

class CallStateReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {

        var proximitySensorListener = ProximitySensorListener(context!!)
        if (context == null || intent == null) {
            return
        }

        if (intent.action == TelephonyManager.ACTION_PHONE_STATE_CHANGED) {
            when (intent.getStringExtra(TelephonyManager.EXTRA_STATE)) {
                TelephonyManager.EXTRA_STATE_RINGING -> {
                    Toast.makeText(context, "Incoming Call", Toast.LENGTH_SHORT).show()
                }

                TelephonyManager.EXTRA_STATE_OFFHOOK -> {
                    Toast.makeText(context, "Call in progress", Toast.LENGTH_SHORT).show()
                    proximitySensorListener.startSensors()
                }

                TelephonyManager.EXTRA_STATE_IDLE -> {
                    Toast.makeText(context, "Call ended", Toast.LENGTH_SHORT).show()
                    proximitySensorListener.stopSensors()
                }
            }
        }
    }
}

class ProximitySensorListener(private val context: Context) : SensorEventListener {
    private var sensorManager: SensorManager =
        context.getSystemService(Context.SENSOR_SERVICE) as SensorManager

    val proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

    fun startSensors() {
        if (proximitySensor != null) {
            sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    fun stopSensors() {
        sensorManager.unregisterListener(this, proximitySensor)
    }

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

    override fun onSensorChanged(event: SensorEvent?) {
        if (event?.sensor?.type == Sensor.TYPE_PROXIMITY) {
            val distance = event.values[0]
            if (distance < 5) {
                println("Off")
            } else {
                println("On")
            }
        }
    }
}

原因分析

核心问题在于每次onReceive被调用时,你都会新建一个ProximitySensorListener实例:

  • 当电话进入OFFHOOK状态时,新建的Listener实例调用startSensors()完成了传感器注册;
  • 当通话结束进入IDLE状态时,又新建了一个全新的Listener实例,调用它的stopSensors()时,实际上是尝试注销这个新实例的监听——但这个实例根本没注册过传感器,自然无法停止之前那个正在运行的Listener。

延迟调用能生效的原因是:延迟期间当前onReceive对应的进程还未被系统回收,此时调用的是同一个Listener实例的stopSensors(),但这只是巧合,并非可靠方案。

解决方案

要解决这个问题,必须保证启动和停止传感器时使用同一个ProximitySensorListener实例,可以通过以下方式实现:

方案1:将Listener设为单例

修改ProximitySensorListener为单例模式,确保全局只有一个实例:

class ProximitySensorListener private constructor(private val context: Context) : SensorEventListener {
    private var sensorManager: SensorManager =
        context.getSystemService(Context.SENSOR_SERVICE) as SensorManager

    val proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

    companion object {
        @Volatile
        private var INSTANCE: ProximitySensorListener? = null

        fun getInstance(context: Context): ProximitySensorListener {
            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: ProximitySensorListener(context.applicationContext).also { INSTANCE = it }
            }
        }
    }

    fun startSensors() {
        if (proximitySensor != null) {
            sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    fun stopSensors() {
        sensorManager.unregisterListener(this, proximitySensor)
    }

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

    override fun onSensorChanged(event: SensorEvent?) {
        if (event?.sensor?.type == Sensor.TYPE_PROXIMITY) {
            val distance = event.values[0]
            if (distance < 5) {
                println("Off")
            } else {
                println("On")
            }
        }
    }
}

然后在CallStateReceiver中获取单例实例:

override fun onReceive(context: Context?, intent: Intent?) {
    if (context == null || intent == null) {
        return
    }
    val proximitySensorListener = ProximitySensorListener.getInstance(context)
    // 后续逻辑不变
}

方案2:动态注册BroadcastReceiver并持有Listener引用

如果是动态注册CallStateReceiver,可以在Receiver类中初始化一次Listener,而不是每次onReceive都新建:

class CallStateReceiver : BroadcastReceiver() {
    private lateinit var proximitySensorListener: ProximitySensorListener

    fun init(context: Context) {
        proximitySensorListener = ProximitySensorListener(context)
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        if (context == null || intent == null) {
            return
        }
        when (intent.getStringExtra(TelephonyManager.EXTRA_STATE)) {
            TelephonyManager.EXTRA_STATE_RINGING -> {
                Toast.makeText(context, "Incoming Call", Toast.LENGTH_SHORT).show()
            }

            TelephonyManager.EXTRA_STATE_OFFHOOK -> {
                Toast.makeText(context, "Call in progress", Toast.LENGTH_SHORT).show()
                proximitySensorListener.startSensors()
            }

            TelephonyManager.EXTRA_STATE_IDLE -> {
                Toast.makeText(context, "Call ended", Toast.LENGTH_SHORT).show()
                proximitySensorListener.stopSensors()
            }
        }
    }
}

动态注册时先调用init()方法初始化Listener:

val receiver = CallStateReceiver()
receiver.init(this)
registerReceiver(receiver, IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:23:13