30

I have very simple Android app: in activity I have a button and I start/stop the OrientationListener. However, after unregistering it, in ddms I can still see the thread android.hardware.SensorManager$SensorThread] (Running).

The registration code:

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
if (sensors.size() > 0)
{
    sensor = sensors.get(0);
    running = sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}

and unregistration:

try
{
    if (sensorManager != null && sensorEventListener != null)
    {
        sensorManager.unregisterListener(sensorEventListener,sensor);
        running = false;
    }
}
catch (Exception e)
{
    Log.w(TAG, e.getMessage());
}

The unregisterListener() method does get executed, however it doesn't kill the sensors thread very often, which keeps running and draining the battery. After few hours my app is listed with 20-30% battery drain. How is that possible? How can I make sure, that the sensor gets unregistered? I don't get any exceptions nor any errors in the logcat. I tried running the listener in the Service - same thing.

jacek
  • 947
  • 1
  • 11
  • 20
  • I had a similar problem, I solved it here http://stackoverflow.com/questions/16504474/android-thread-cannot-unregister-sensoreventlistener [1]: http://stackoverflow.com/questions/16504474/android-thread-cannot-unregister-sensoreventlistener – tricknology May 13 '13 at 05:05

7 Answers7

5

In my case, to solve this problem I needed to correct the context I was using to get SensorManager. I was inside a Service, and I needed to get the SensorManager this way:

SensorManager sensorManager = (SensorManager) getApplicationContext().getSystemService(SENSOR_SERVICE);

So, just certify if you are getting the SensorManager the correct way.

Vitor Braga
  • 2,173
  • 1
  • 23
  • 19
  • 1
    Solid advice here! This was 1 of 3 issues of mine. Ref other comments I've made in this post. – XMAN Nov 06 '17 at 14:46
  • 1
    My problem too, `applicationContext.getSystemService()` and `Service.getSystemService()` returned two different `SensorManager`s and made it not working! THANKS! But my question then: which one should be used within a service ? – Hendy Irawan Jan 31 '18 at 07:00
  • Man, I spent 2 hours on this. Thank you – Scott Driggers May 10 '21 at 20:54
3

You don't show enough code to tell for sure, but perhaps your test

if (sensorManager != null && sensorEventListener != null)

simply is not accurate. That is, sensorManager or sensorEventListener may be null when you are still registered as listening.

CvR
  • 151
  • 1
  • 7
  • Good point here. This threw me off- especially when registering sensors in a background thread (without proper context). – XMAN Nov 06 '17 at 14:47
2

I faced similar issue.

Workaround to stop the sensor.

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
    }
}
Hemant G
  • 633
  • 5
  • 13
2

Try setting the manager to null

sensorManager.unregisterListener(sensorEventListener,sensor);
sensorManager = null;

and then obtain the manager again when you need it. This should make the thread finish consistently (it did in my case). I haven't found any documentation explaining this behavior but would be interested in hearing about it.

  • Following this advise (and others in this SO post) did the trick for me. This was part of the overall issue. I also: 1) I also implemented proper boolean checks in places like registration, unregistration and onSensorChanged(). 2) Kept proper Context. – XMAN Nov 06 '17 at 14:42
2

I'm having the same problem. I checked the Android code. The relevant code is in SensorManager.java

private void unregisterListener(Object listener) {
    if (listener == null) {
        return;
    }

    synchronized (sListeners) {
        final int size = sListeners.size();
        for (int i=0 ; i<size ; i++) {
            ListenerDelegate l = sListeners.get(i);
            if (l.getListener() == listener) {
                sListeners.remove(i);
                // disable all sensors for this listener
                for (Sensor sensor : l.getSensors()) {
                    disableSensorLocked(sensor);
                }
                break;
            }
        }
    }
}

and

        public void run() {
            //Log.d(TAG, "entering main sensor thread");
            final float[] values = new float[3];
            final int[] status = new int[1];
            final long timestamp[] = new long[1];
            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);

            if (!open()) {
                return;
            }

            synchronized (this) {
                // we've open the driver, we're ready to open the sensors
                mSensorsReady = true;
                this.notify();
            }

            while (true) {
                // wait for an event
                final int sensor = sensors_data_poll(sQueue, values, status, timestamp);

                int accuracy = status[0];
                synchronized (sListeners) {
                    if (sensor == -1 || sListeners.isEmpty()) {
                        // we lost the connection to the event stream. this happens
                        // when the last listener is removed or if there is an error
                        if (sensor == -1 && !sListeners.isEmpty()) {
                            // log a warning in case of abnormal termination
                            Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
                        }
                        // we have no more listeners or polling failed, terminate the thread
                        sensors_destroy_queue(sQueue);
                        sQueue = 0;
                        mThread = null;
                        break;
                    }
                    final Sensor sensorObject = sHandleToSensor.get(sensor);
                    if (sensorObject != null) {
                        // report the sensor event to all listeners that
                        // care about it.
                        final int size = sListeners.size();
                        for (int i=0 ; i<size ; i++) {
                            ListenerDelegate listener = sListeners.get(i);
                            if (listener.hasSensor(sensorObject)) {
                                // this is asynchronous (okay to call
                                // with sListeners lock held).
                                listener.onSensorChangedLocked(sensorObject,
                                        values, timestamp, accuracy);
                            }
                        }
                    }
                }
            }
            //Log.d(TAG, "exiting main sensor thread");
        }
    }
}

So it looks like the thread should be terminated when there are no listeners any more

kingston
  • 11,053
  • 14
  • 62
  • 116
  • I am also having the same problem http://stackoverflow.com/questions/16504474/android-thread-cannot-unregister-sensoreventlistener – tricknology May 12 '13 at 05:28
1

Could be a problem with scope. Try logging the values of sensorEventListener and sensor when you register and unregister them. (.toString()) to make sure that they are the same.

Mobius
  • 699
  • 4
  • 11
  • That's not it. They are exactly the same. – jacek Aug 05 '11 at 21:08
  • This was key advice. Adding good DEBUG helped me chase down several issues, 2 big issues being: 1) Proper registration/unregister (within Context), 2) Implement proper boolean checks in places like registration, unregistration and onSensorChanged(). 3) I implemented registration in a background thread (beware!). – XMAN Nov 06 '17 at 14:45
0
//Declaration of SensorManager
private SensorManager sensorManager;
private Sensor mAccelerometer;
private SensorEventListener sensorEventListener;

onCreate call To

try {
         sensorManager = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE);
        sensorManager.registerListener(sensorEventListener=new SensorEventListener() {
            int orientation = -1;

            @Override
            public void onSensorChanged(SensorEvent event) {
                if (event.values[1] < 6.5 && event.values[1] > -6.5) {
                    if (orientation != 1) {
                        Log.d("Sensor", "Landscape");
                    }
                    orientation = 1;
                } else {
                    if (orientation != 0) {
                        Log.d("Sensor", "Portrait");


                    }
                    orientation = 0;
                }
            }

            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }
        },
                mAccelerometer=sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
    } catch (Exception e) {
        e.printStackTrace();
    }

Please call to on Destroy

if (sensorManager != null && mAccelerometer != null) 
{ 
    sensorManager.unregisterListener(sensorEventListener, mAccelerometer); sensorManager = null; 
}
HebeleHododo
  • 3,620
  • 1
  • 29
  • 38