Как решить проблему ANR, возникшую в SensorEventListener, работающем в отдельном потоке?

Я новичок в программировании Java для Android (на самом деле я новичок в общем компьютерном программировании). Я намеревался сделать SensorEventListener датчика акселерометра в потоке, отдельном от основного потока в Service. Метод onSencorChanged хорошо работает в отдельном потоке, однако вскоре после запуска службы возникает ошибка ANR. Хотя ANR происходит, если я не завершаю работу в момент возникновения ANR и не нажимаю «подождать», чтобы приложение продолжало работать, приложение работает хорошо, как я и предполагал. Меня долго пытались найти, как решить эту проблему, но я не могу, наконец, заставить мое приложение перестать возникать ANR.

Это мой код, использующий 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) { }
    }
}

Я намеревался показать вам только часть, связанную с вопросом о коде, но я впервые публикую вопрос о переполнении стека, поэтому, если мой код слишком длинный, извините.


person PGD    schedule 10.12.2020    source источник
comment
Здесь вы можете прочитать об Услугах и понять, как они должны работать: developer.android.com /reference/android/app/Service.html   -  person keybee    schedule 10.12.2020


Ответы (1)


Основная причина

В Android служба работает в основном потоке, которому уже назначен Looper. В вашем коде, когда служба запускается, она вызывает Looper.prepare() и Looper.loop() в основном потоке, это приведет к неожиданному поведению вашего приложения.

Решение

Удалите следующий код из класса SwingArmSensorService

if (Looper.myLooper() == null) {
    Looper.prepare();
}

Looper.loop();

В результате ваш код будет таким:

@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);
    ...
}
person Son Truong    schedule 11.12.2020