Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android's SensorManager fails to effectively unregister listeners

Tags:

android

My app creates a Service that overrides onStartCommand to create a SensorManager and attach a SensorEventListener to one of the sensors:

public class AccelerometerService extends Service {
    private SensorManager sensorManager;
    private Sensor sensor;
    private SensorEventListener activeListener; 
    // ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startid) {
        // ...

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
        activeListener = new SensorEventListener() {
            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {

            }

            @Override
            public void onSensorChanged(SensorEvent event) {
                Log.i("PluckLock", "" + event.values[0]);
            }
        };

        sensorManager.registerListener(activeListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);

        return START_STICKY;
    }

    // ...
}

The full code is available on GitHub.

I want to override Service's onDestroy() method and, in it, kill the sensor:

sensorManager.unregisterListener(activeListener);

But when I do this, nothing actually happens! My onSensorChanged() listener continues to receive correct values from the accelerometer (or software LINEAR_ACCELERATION sensor, in my case). The problem, of course, is quoted here in the docs:

Always make sure to disable sensors you don't need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours.

My app only needs to ping the accelerometer for as long as the screen is actually unlocked. I've implemented (and verified the correct function of) BroadcastReceivers that destroy the Service when the phone is locked, successfully calling its onDestroy() method. But nothing actually happens once I try to unregister the listeners.

I've found a few others asking this question, with no resolution.

  • Unregistering SensorManager doesn't work
  • How to unregister a listener from a sensor after stopping the service?
  • SensorEventListener not unregistering itself
  • Android sensor listener does not get unregistered or sensor still retrieves values
  • SensorEventListener doesn't get unregistered with unregisterListener() method

I can only assume it is an API bug, and that there is really no good way to kill these sensors once I've started grabbing data from them. I have tried setting the SensorManager, Sensor, and SensorEventListener to null, as well as trying to register a null sensor listener with the manager. None of these have any effect on the polling.

My current "workaround" has almost no effect on battery life. I have a private boolean in my class's definition that I set to true or false depending on whether I want sensor values. In my onSensorChanged() method, I immediately return if the sensor listener is supposed to be inactive. But by then, it is too late: the brunt of the work being performed by the OS (getting sensor values) occurs before I can manage to leave the onSensorChanged() method, so battery life is still horrendous for as long as my service is running.

My real question here, barring some miracle real solution, is what can I do to stop getting sensor values? Obviously, unregistering the sensor listener does not work at this point; even setting everything to null and destroying the service has no effect. But there must be some way to indicate to the operating system that my app doesn't require use of the sensor values anymore. Is there some way to kill my entire app, only restarting it when the phone is unlocked? Maybe that would let the operating system know that my app's not in need of values anymore. Really, any solution that legitimately stops polling the sensor but gives me some way to restart it, even if the code is ugly, would make me happy.

The minimum API level for my project is 9.

like image 412
Timothy J. Aveni Avatar asked Oct 20 '22 22:10

Timothy J. Aveni


1 Answers

I faced similar issue.

Workaround to stop the sensor from draining the battery.

I used a static boolean mIsSensorUpdateEnabled. Set it to 'false' when you want to stop getting values from sensors. And in onSensorChanged() method, check the value of the boolean variable and again make to call to unregister the sensors. And this time, it works. The sensors would be unregistered and you will no longer get onSensorChanged callback.

    public class MainActivity extends Activity implements SensorEventListener{
        private static boolean mIsSensorUpdateEnabled = false;
        private SensorManager mSensorManager;
        private Sensor mAccelerometer;

        @override
        protected void onCreate(){
            mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        }

        private startSensors(){
            mAccelerometer =  mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            int delay = 100000; //in microseconds equivalent to 0.1 sec
            mSensorManager.registerListener(this,
                        mAccelerometer,
                        delay
                );
            mIsSensorUpdateEnabled =true;
        }

        private stopSensors(){
             mSensorManager.unregisterListener(this, mAccelerometer);
             mIsSensorUpdateEnabled =false;
        }


        @Override
        public void onSensorChanged(SensorEvent event) {
            if (!mIsSensorUpdateEnabled) {
                stopSensors();
                Log.e("SensorMM", "SensorUpdate disabled. returning");
                return;
            }
            //Do other work with sensor data
        }
    }`
like image 103
Hemant G Avatar answered Oct 22 '22 12:10

Hemant G