当设备休眠时,Android 会减慢前台服务的速度

Ler*_*ohr 8 android android-sensors

几天来,我一直在努力解决以下问题:我想在屏幕关闭时使用 Android 设备上的重力传感器计算运动模式。我正在使用我在前台启动的绑定服务(带有 Android 8 的通知以使其保持运行),并且在屏幕打开时一切正常。即使应用程序不在前台运行,一切正常。

然而,一旦显示器关闭,非常奇怪的事情就会开始发生:传感器数据仍在处理中,并且在一定程度上计算了移动,但结果非常糟糕且不准确。此外,如果再次打开屏幕,该应用程序的行为非常奇怪。有时应用程序按预期工作,但有时似乎旧传感器事件被延迟处理并在以后计数。从这个时候开始,整个算法就不能顺利运行了。还有什么有趣的:如果设备已插入并且我在 Android Studio 中使用控制台观察一切,一切都运行得非常完美,即使屏幕关闭也是如此。但是,如果拔掉设备,结果又会出错。

我尝试了很多事情:在主线程上运行所有内容,在另一个线程上,使用 IntentService,将 App 设置为Doze的白名单,在屏幕关闭时不向 MainActivity 发送数据,并遵循本指南(我正在开发 Galaxy J5) - 但没有任何效果。似乎 Android 系统的 SensorFusion 算法在待机状态下关闭,即使有注册的监听器,或者三星在后台运行了一些电池优化并限制了 CPU 操作。还有其他可能导致这种行为的原因吗?因此,如果应用程序处于活动状态,如果它在后台运行,但在设备处于睡眠状态时运行良好?

值得一提的是:我正在为 Cordova 开发一个插件。

这是我的代码

主类

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) {
        ...
    };

}
Run Code Online (Sandbox Code Playgroud)

服务类

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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑

在此期间我测试了很多。我现在使用Apache Commons Primitives Collections以获得更好的性能(而不是大型 FastUtil,这也导致了这个过去错误)。

我还在两台设备上测试了该应用程序,一台相当老的 LG G2 和 Galaxy J5。两者的问题是一样的。所以可能不是制造商特定的。Android Studio Profiler 报告两种设备的 CPU 使用率平均为 1-3%,因此我认为过载可能不是原因。我还测试了一个 TimerTask 和 Timer,而不是一个 Runnable 和 Handler,但效果不佳。

同样非常有趣的是:我尝试按照此处的说明通过 Wifi 调试应用程序,即使设备休眠且屏幕关闭,该应用程序也能正常运行。调试这个问题非常困难,因为即使没有连接电缆,应用程序在我调试它时也能正常运行。我不知道我还能做什么。

Ler*_*ohr 6

对于有类似问题的每个人:我终于找到了解决方案。

我觉得在Android文档中没有很好的解释,也不是很直观,但是还是需要设置一个部分唤醒锁来防止CPU休眠。解释了如何设置唤醒锁。仅靠前台服务不足以保持对时间要求严格的进程运行。