首页 > 解决方案 > 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,它运行一个传感器监听器。

我的问题如下:我可以指定任何选项,还是通过更改获取数据的方法在后台不丢失?(当我的应用程序在后台运行并且屏幕关闭时。)

标签: androidwear-osaccelerometergyroscopesamsung-galaxy

解决方案


推荐阅读