Android замедляет работу переднего плана, когда устройство спит - PullRequest
3 голосов
/ 14 марта 2019

Вот уже несколько дней я борюсь со следующим: я хочу подсчитывать шаблоны движения, используя датчик силы тяжести на устройстве Android, когда экран выключен. Я использую связанный сервис, который запускаю на переднем плане (с уведомлением для Android 8, чтобы он продолжал работать), и все работает нормально, когда экран включен. Даже если приложение не работает на переднем плане, все работает нормально.

Однако, как только дисплей отключается, начинают происходить очень странные вещи: данные датчика все еще обрабатываются, и движение учитывается до некоторой степени, но результаты очень плохие и неточные. Кроме того, если экран снова включается, приложение ведет себя очень странно. Иногда приложение работает должным образом, но иногда кажется, что старые события датчика обрабатываются с задержкой и учитываются позже. С этого момента весь алгоритм не работает гладко. Что также интересно: если устройство подключено, и я наблюдаю все с помощью консоли в Android Studio, все работает просто отлично, даже если экран выключен. Однако, если устройство отключено, результаты снова становятся неправильными.

Я перепробовал много вещей: запуск всего в главном потоке, в другом потоке, с использованием IntentService, установка приложения в белый список для Doze , не отправка данных в MainActivity, пока экран выключил и следовал этому руководству (я разрабатываю на Galaxy J5) - но ничего не получалось. Похоже, что либо алгоритмы SensorFusion системы Android отключаются в режиме ожидания, даже если зарегистрированный прослушиватель, либо Samsung выполняет некоторую оптимизацию работы аккумулятора в фоновом режиме и ограничивает операции ЦП. Есть ли что-то еще, что может вызвать такое поведение? Так работает нормально, если приложение активно, если оно в фоновом режиме, но не когда устройство находится в спящем режиме?

Также может быть интересно упомянуть: я разрабатываю плагин для Cordova.

Это мой код

Main.class

public class SensorPlugin extends CordovaPlugin implements ServiceClass.Delegate {

    public void initialize() {
      ...
      Intent serviceIntent = new Intent(applicationContext, ServiceClass.class);
      applicationContext.bindService(serviceIntent, serviceConnection, 
          Context.BIND_AUTO_CREATE);
    }

    public void start() {
        serviceClass.startMeasuring();
    }

    @Override
    public void updateMovementCount(int count) {
        ...
    };

}

Service.class

public class ServiceClass extends Service implements OtherClass.Delegate {

    private volatile boolean isMeasuring;
    private volatile double gravityX;
    private volatile double gravityY;
    private volatile double gravityZ;

    public IBinder onBind(Intent intent) {
        sensorManager = (SensorManager) getApplicationContext().
            getSystemService(Context.SENSOR_SERVICE);

        startForeground(1, buildNotification());
        return mBinder;
    }

    public void startMeasuring() {
        assert sensorManager != null;
        Sensor gravity = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
        sensorManager.registerListener(this, gravity, 
            SensorManager.SENSOR_DELAY_GAME);

        isTracking = true;

        SensorThread sensorThread = new SensorThread();
        sensorThread();
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        gravityX = event.values[0];
        gravityY = event.values[1];
        gravityZ = event.values[2];
    }

    // This is an interface method from another class that I wrote
    // (see below). For which I set this class as delegate and as soon
    // as the other class finds a pattern in the data it calls this method
    @Override
    public void movementPatternDidChange(int count) {
        // Usually I send this count to the main class with
        // the same delegation pattern
        delegate.updateMovementCount(count);
    }

    class SensorProcessingThread extends Thread {
        Handler handler = new Handler();

        // I use another runnable here, as I only want to process 10
        // sensor events per second. The sensor manager usually returns
        // way more which I don't need.
        private Runnable sensorProcessingRunnable = new Runnable() {
            public void run() {
                otherClass.processMotionData(gravityX, gravityY, gravityZ);
                if (isMeasuring) {
                    handler.postDelayed(this, 100);
                }
            }
        };

        @Override
        public void run() {
            if (!isMeasuring) {
                return;
            }

            handler.postDelayed(sensorProcessingRunnable, 100);
        }
    }

}

Редактировать

Тем временем я много тестировал. Сейчас я использую Apache Commons Primitives Collections для лучшей производительности (вместо большого FastUtil, который также вызывал эту ошибку в прошлом).

Я также тестировал приложение на двух устройствах: довольно старом LG G2 и Galaxy J5. Проблема одинакова на обоих. Так что, вероятно, не зависит от производителя. Android Studio Profiler сообщает об использовании ЦП в среднем по 1-3% на обоих устройствах, поэтому я предполагаю, что перегрузка, скорее всего, не является причиной. Я также протестировал TimerTask и Timer вместо Runnable и Handler, которые также не работали.

Что также очень интересно: я попытался отладить приложение по Wi-Fi , как описано здесь , и приложение работает абсолютно нормально, даже если устройство спит и экран выключен. Отладить эту проблему очень сложно, потому что приложение работает нормально, как только я отлаживаю его даже без подключенного кабеля. Я понятия не имею, что я мог бы сделать еще.

1 Ответ

1 голос
/ 16 марта 2019

Для всех с похожей проблемой: Я наконец нашел решение.

Я думаю, что это не очень хорошо объяснено в документации по Android, и не очень интуитивно понятно, но все же необходимо установить частичную блокировку пробуждения, чтобы не спать ЦП. Это объясняет, как установить блокировку от пробуждения. Одной службы переднего плана недостаточно для поддержания процесса, критичного ко времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...