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
相关产品推荐
相关产品推荐

