几天来,我一直在努力解决以下问题:我想在屏幕关闭时使用 Android 设备上的重力传感器来计算运动模式。我正在使用在前台启动的绑定服务(带有 Android 8 的通知以使其保持运行),并且当屏幕打开时一切正常。即使应用程序没有在前台运行,一切也正常。
然而,一旦显示器关闭,非常奇怪的事情就开始发生:传感器数据仍在处理,并且在一定程度上对运动进行计数,但结果非常糟糕且不准确。此外,如果屏幕再次打开,该应用程序的行为会非常奇怪。有时应用程序按预期工作,但有时似乎旧的传感器事件被延迟处理并稍后计数。整个算法从这个时候开始就运行得不太顺利了。同样有趣的是:如果设备已插入,并且我通过 Android Studio 中的控制台观察到一切,那么即使屏幕关闭,一切也会完美运行。然而,如果设备被拔掉,结果又会变得错误。
我尝试了很多事情:在主线程上运行所有内容,在另一个线程上运行,使用 IntentService,将应用程序设置在白名单上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) {
...
};
}
服务等级
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);
}
}
}
Edit
在此期间我进行了很多测试。我现在正在使用Apache Commons 基元集合为了更好的性能(而不是大型 FastUtil,这也会导致this过去的错误)。
我还在两台设备上测试了该应用程序,一台相当旧的 LG G2 和 Galaxy J5。两者的问题都是一样的。所以可能不是制造商特定的。 Android Studio Profiler 报告两台设备上的 CPU 使用率平均为 1-3%,因此我认为过载可能不是原因。我还测试了 TimerTask 和 Timer,而不是 Runnable 和 Handler,但效果不佳。
也很有趣的是:我尝试通过 Wifi 调试应用程序正如这里所解释的,即使设备处于睡眠状态并且屏幕关闭,该应用程序也可以正常工作。调试这个问题非常困难,因为即使没有连接电缆,只要我调试它,应用程序就表现良好。我不知道我还能做什么。