android - Wear OS 设备上的数据频率不稳定
问题描述
我有从传感器收集数据的设备:
型号:XMWT01
设备名称:小米手表 78CC 版本
号:PXDP.210508.001.XM129
型号:WG12026
设备名称:TicWatch E2 0846内部版本
号:PWDR.190618.001.B8
型号:SM-
R860 设备名称:Galaxy Watch4内部版本
号:R860XXU1BUH9
对于此设备,我注册加速度计和陀螺仪以收集 50 Hz 频率的数据(例如):
int samplingPeriodUs = 20_000;
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE)
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, sensor, samplingPeriodUs, 0);
我在后台服务中运行侦听器,并使用 ExecutorService 将数据文件保存在单独的线程中。收集并上传数据后,我进行验证。
我的服务:
////
public class ReaderSensorService extends Service implements SensorEventListener, LocationListener {
public static final String CHANNEL_ID = "channel_reader_service";
public static final int NOTIFY_ID = 1001;
private static final LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.getAppContext());
private static final UserPreferences userPreferences = UserPreferences.getInstance();
private final DataCollector dataCollector = DataCollector.getInstance();
private final Map<Integer, SensorData> mapSensors = new HashMap<>();
private static SensorManager sensorManager = null;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
switch (intent.getAction()) {
case ACT_START_FOREGROUND:
createNotificationChannel();
Notification notification = createNotification(intent);
startForeground(NOTIFY_ID, notification);
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
int periodMs = userPreferences.getSensorPeriodMeasurement(SensorType.GNSS);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, periodMs, 0, this);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, periodMs, 0, this);
}
States state = start();
if (state == States.SENSOR_AVAILABLE) {
return START_STICKY;
}
intentErrorMessage(state);
case ACT_STOP_FOREGROUND:
stop();
stopForeground(true);
stopSelf();
break;
default:
break;
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
}
@Override
public void onDestroy() {
stop();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private Notification createNotification(Intent intent) {
Intent intentNotify = new Intent(this, MainActivity.class);
intentNotify.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intentNotify, PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(intent.getStringExtra("inputExtra"))
.setContentText("Service is running in background")
.setWhen(0)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
}
private void createNotificationChannel() {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
private void intentErrorMessage(States state) {
Intent intent = new Intent(ACT_UPLOAD_DATA);
switch (state) {
case DEVICE_NO_MAC:
intent.putExtra(BROADCAST_MESSAGE, MSG_NOT_MAC_AVAILABLE);
break;
case SENSOR_ALL_UNAVAILABLE:
intent.putExtra(BROADCAST_MESSAGE, MSG_NOT_SUPPORTED);
break;
case SENSOR_NO_ACC:
intent.putExtra(BROADCAST_MESSAGE, MSG_NOT_SUPPORTED_ACC);
break;
case SENSOR_NO_GYRO:
intent.putExtra(BROADCAST_MESSAGE, MSG_NOT_SUPPORTED_GYRO);
break;
default:
break;
}
localBroadcastManager.sendBroadcast(intent);
}
public States start() {
int code = 0;
String macAddress = IdentifyUtils.getMACAddress();
if (macAddress.isEmpty()) {
return States.DEVICE_NO_MAC;
}
List<SensorType> sensorList = Arrays.stream(SensorType.values())
.filter(sensorType -> sensorType.getID() != Sensor.TYPE_ALL)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
for (SensorType sensorType : sensorList) {
if (!register(sensorType)) {
code |= States.getCode(sensorType);
}
}
if (code == States.SENSOR_ALL_UNAVAILABLE.getCode()) {
return States.SENSOR_ALL_UNAVAILABLE;
}
return States.SENSOR_AVAILABLE;
}
public void stop() {
unregister();
dataCollector.shutdown();
}
public static Boolean checkSupportSensor(int typeSensor) {
Sensor sensor = sensorManager.getDefaultSensor(typeSensor);
return sensor != null;
}
private Boolean register(SensorType sensorType) {
boolean status = false;
if (userPreferences.isEnableSensor(sensorType)) {
int periodMs = userPreferences.getSensorPeriodMeasurement(sensorType);
String mac = IdentifyUtils.getMACAddress();
if (sensorType != SensorType.GNSS) {
Sensor sensor = sensorManager.getDefaultSensor(sensorType.getID());
if ((status = sensor != null)) {
sensorManager.registerListener(this, sensor, 20_000 /* temp value for fix 50 Hz */, 0);
} else {
return false;
}
}
DeviceData deviceData = dataCollector.getDevice(mac);
SensorData sensorData = deviceData.addSensor(sensorType);
mapSensors.put(sensorType.getID(), sensorData);
}
return status;
}
private void unregister() {
sensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;
long timestamp = event.timestamp;
int sensorType = event.sensor.getType();
SensorData sensorData = mapSensors.get(sensorType);
synchronized (this) {
switch (sensorType) {
case Sensor.TYPE_ACCELEROMETER:
case Sensor.TYPE_GYROSCOPE:
case Sensor.TYPE_MAGNETIC_FIELD:
sensorData.add(new FloatVector3(timestamp, values[0], values[1], values[2]));
break;
case Sensor.TYPE_HEART_RATE:
case Sensor.TYPE_LIGHT:
sensorData.add(new FloatVector1(timestamp, values[0]));
break;
default:
break;
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onLocationChanged(@NonNull Location location) {
float[] values = {(float) location.getAltitude(), (float) location.getLatitude(), (float) location.getLongitude()};
SensorData sensorData = mapSensors.get(SensorType.GNSS.getID());
sensorData.add(new DoubleVector3(location.getElapsedRealtimeNanos(), values[0], values[1], values[2]));
}
}
我计算每秒的计数并绘制加速度计和陀螺仪 7-8 小时。结果如附图(test_samsung_gw4.png)所示。从图中可以看出,我遇到了大数据缺失。但是,当我将应用程序的主窗口留在屏幕上时(主活动中的 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)),我以稳定的频率获取数据并且没有数据丢失。
我在信中附上了我的部分代码。最重要的类是 ReaderSensorService,它运行一个传感器监听器。
我的问题如下:我可以指定任何选项,还是通过更改获取数据的方法在后台不丢失?(当我的应用程序在后台运行并且屏幕关闭时。)
解决方案
推荐阅读
- c - 函数调用的后递增函数参数
- windows - 指向结构的 Qt 指针
- c# - 如何使用从 Mediator 检索到的 EFCore 更新对象
- r - 用于从 R 中的列表创建数据帧的循环
- c# - Discord.net C# Bot - 一些分片连续连接和断开连接
- javascript - Javascript中的括号优先级
- javascript - 从 api (x-api-key) 检索用户数据
- xml-namespaces - SaxonJS 发出多余的命名空间属性
- php - 如何使用 Phpspreadsheet 将数组直接写入 XSLS 文件
- java - 如何根据列表中的分数获取前 7 名