I'm noob at Java Android Programming(actually I'm noob at overall computer programming). I intended to make SensorEventListener of Accelometer sensor in a thread separate from main thread at Service. onSencorChanged method works well in separate thread, however, not long after operating Service, ANR occurs. Although ANR occurs, if I don't terminate at the point of ANR occures and press 'wait' to keep application running, the application goes well as I intended. I have been tried to find how to resolve the problem for a long time, but I can't finally make my application stop occuring ANR.
This is my code using HandleThread.
public class SwingArmSensorService extends Service {
private SensorManager mSensorManager;
private Sensor mSensor;
private AccelerometerListenerInService listener;
private HandlerThread mSensorThread;
private Handler mSensorHandler;
private static SuperActivityUsingServiceMain activity;
private int mSteps;
private final String SWING_SENSOR_INTENT_STEP_DATA_NAME = "steps";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Looper.myLooper() == null) {
Looper.prepare();
}
mSensorThread = new HandlerThread("Sensor Thread", Thread.MAX_PRIORITY); //
mSensorThread.start(); //
mSensorHandler = new Handler(mSensorThread.getLooper());
listener = new AccelerometerListenerInService();
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(listener, mSensor, SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
mSteps = intent.getIntExtra(SWING_SENSOR_INTENT_STEP_DATA_NAME, 0);
Looper.loop();
...
}
private class AccelerometerListenerInService implements SensorEventListener {
private Handler mainHandler;
private float previousY, currentY;
private float x, y, z;
private final float THRESHOLD = 10f;
public AccelerometerListenerInService() {
mainHandler = new Handler(Looper.getMainLooper());
}
@Override
public void onSensorChanged(SensorEvent event) {
x = event.values[0];
y = event.values[1];
z = event.values[2];
currentY = y;
if (Math.abs(currentY - previousY) > THRESHOLD) {
mSteps++;
// Handle UI at main thread here
mainHandler.post(new Runnable() {
@Override
public void run() {
activity.setTextView(mSteps);
}
});
}
previousY = y;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
}
}
I intended to show you only a partion related to question of code, but it's first time to post a question Stack Overflow, so if my code is too long I'm sorry.
Root cause
In Android, a Service is running on the main thread which already has a Looper assign with it. In your code, when the service is started, it calls Looper.prepare()
and Looper.loop()
on the main thread, it will cause unexpected behavior with your app.
Solution
Delete the following code from SwingArmSensorService
class
if (Looper.myLooper() == null) {
Looper.prepare();
}
Looper.loop();
As the result, your code will be:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mSensorThread = new HandlerThread("Sensor Thread", Thread.MAX_PRIORITY); //
mSensorThread.start(); //
mSensorHandler = new Handler(mSensorThread.getLooper());
listener = new AccelerometerListenerInService();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(listener, mSensor, SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
mSteps = intent.getIntExtra(SWING_SENSOR_INTENT_STEP_DATA_NAME, 0);
...
}