Вот уже несколько дней я борюсь со следующим: я хочу подсчитывать шаблоны движения, используя датчик силы тяжести на устройстве 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 , как описано здесь , и приложение работает абсолютно нормально, даже если устройство спит и экран выключен. Отладить эту проблему очень сложно, потому что приложение работает нормально, как только я отлаживаю его даже без подключенного кабеля. Я понятия не имею, что я мог бы сделать еще.