0

我有一项服务在 Moto 360 的后台持续运行。它以 50Hz 的频率测量加速度计数据。我将样本打包成块(每个块 15,000 个样本 == 5 分钟的数据)并通过 DataApi 将这些块发送到移动设备。一切都很好,但随机我在来自传感器更改侦听器的事件中存在差距。采样间隔可以在 10 小时内出现一次或两次,间隔时间可以是每个间隔 1-10 分钟。

到目前为止做了什么:

  1. 所有采样处理/打包/记录/发送都在单独的线程上完成,因此不会阻塞传感器侦听器。
  2. 服务是粘性的,日志被添加到生命周期回调中,所以我可以确定服务在采样间隙时间是活跃的。
  3. 该服务声明为前台服务以获得更多的 CPU 时间和更高的优先级。
  4. 我在服务的 onCreate 中获取 PARTIAL_WAKE_LOCK 并在 onDestroy 中释放(我知道可能对电池有影响,但此时,连续数据更重要)。

抽样差距的原因和解决方案是什么?

提前致谢。

我的服务代码:

public class MeasurementService extends Service implements SensorEventListener {

public final static int SENS_ACCELEROMETER = Sensor.TYPE_ACCELEROMETER;

private static int mCounter;

private ArrayList<AccelerometerSampleData> mAccelerometerSensorSamples;

@Inject
SensorManager mSensorManager;

@Inject
DataTransferHolder mDataTransferHolder;

@Inject
EventBus mEventBus;

@Inject
WearSharedPrefsController mSharedPrefsController;

@Inject
WearConfigController mConfigController;

@Inject
MyWearLogger mMyWearLogger;

private Sensor mAccelerometerSensor;

private AccelerometerSampleData mLastEventData;

protected HandlerThread handlerThread;

private PowerManager.WakeLock mWakeLock;

@Override
public void onCreate() {
    super.onCreate();
    ((MyWearApplication)getApplication()).getApplicationComponent().inject(this);
    mMyWearLogger.writeToLogFile(DateUtils.getCurrentTimeString() + " MeasurementService: " +
            "onCreate");
    initSensors();
    mEventBus.register(this);
    startThread();
    acquireWakeLock();
    resetPackageValues();
    mSharedPrefsController.setMessagePackageIndex(0);
    startForeground();
    startMeasurement();
    mEventBus.postSticky(new MeasurementServiceStatus(true));
}

private void startForeground() {
    Notification.Builder builder = new Notification.Builder(this);
    builder.setContentTitle("Measurement Service");
    builder.setContentText("Collecting sensor data..");
    builder.setSmallIcon(R.drawable.ic_play_circle_outline_black_48dp);
    startForeground(1, builder.build());
}

private void acquireWakeLock() {
    if (mWakeLock == null || !mWakeLock.isHeld()) {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SensorAdapter Lock");
        mWakeLock.acquire();
    }
}

private void releaseWakeLock() {
    if (mWakeLock != null && mWakeLock.isHeld()) {
        mWakeLock.release();
        mWakeLock = null;
    }
}

@SuppressWarnings("unused")
@Subscribe
public void onSamplingRateUpdated(UpdateSamplingRateMessage updateSamplingRateMessage) {
    stopMeasurement();
    startMeasurement();
}

@SuppressWarnings("unused")
@Subscribe
public void onSamplesPerPackageLimitUpdated(UpdateChunkLimitMessage chunkLimitMessage) {
    stopMeasurement();
    startMeasurement();
}

private void startThread() {
    if (handlerThread == null) {
        handlerThread = new HandlerThread(this.getClass().getSimpleName() + "Thread");
    }
    if (handlerThread.getState() == Thread.State.NEW) {
        handlerThread.start();
    }
}

private void resetPackageValues() {
    mCounter = 0;
    mAccelerometerSensorSamples = new ArrayList<>();
}

private void initSensors() {
    Timber.d("initiating sensors");

    if (mAccelerometerSensor == null) {
        mAccelerometerSensor = mSensorManager.getDefaultSensor(SENS_ACCELEROMETER, true);
        if (mAccelerometerSensor == null){
            mAccelerometerSensor = mSensorManager.getDefaultSensor(SENS_ACCELEROMETER);
        }
    }

}

private void startMeasurement() {
    Timber.d("starting measurement");
    if (checkNotNull()) {
        Timber.d("sensors are valid, registering listeners");
        Handler handler = new Handler(handlerThread.getLooper());
        // This buffer is max 300 on Moto 360, so we use 250;
        int maxSamplesBuffer = 250 * mConfigController.getSamplingRateMicrosecond();
        mSensorManager.registerListener(this,
                mAccelerometerSensor,
                mConfigController.getSamplingRateMicrosecond(),
                maxSamplesBuffer,
                handler);
    } else {
        Timber.w("sensors are null");
    }
}

private void stopMeasurement() {
    mSensorManager.unregisterListener(this);
}

private boolean checkNotNull() {
    Timber.d("checking sensors validity");
    return mSensorManager != null
            && mAccelerometerSensor != null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    mMyWearLogger.writeToLogFile(DateUtils.getCurrentTimeString() + " MeasurementService: " +
            "onStartCommand");
    return START_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    mMyWearLogger.writeToLogFile(DateUtils.getCurrentTimeString() + " MeasurementService: " +
            "onDestroy");
    stopMeasurement();
    releaseWakeLock();
    stopThread();
    mEventBus.unregister(this);
    mEventBus.postSticky(new MeasurementServiceStatus(false));
}

private void stopThread() {
    if (handlerThread != null) {
        handlerThread.quit();
    }
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onSensorChanged(SensorEvent newEvent) {

    AccelerometerSampleData newEventData = new AccelerometerSampleData(
            System.currentTimeMillis() + ((newEvent.timestamp - SystemClock.elapsedRealtimeNanos()) / 1000000L),
            newEvent.values[0],
            newEvent.values[1],
            newEvent.values[2]);

    if (DefaultConfiguration.LOG_EACH_SAMPLE) {
        logData(newEventData, calculateTimeDiff(newEventData));
    }

    addNewEventToPackage(newEventData);
    updateCurrentValues(newEventData);

    if (mCounter >= mConfigController.getSamplesPerChunk()) {
        float batteryPercentage = getBatteryStatus();
        sendPackageToMobileDevice(batteryPercentage);
        resetPackageValues();
    }
}

private void sendPackageToMobileDevice(float batteryPercentage) {
    Timber.i("sending package to processing service");

    long messagePackageId = System.currentTimeMillis();
    MessagePackage messagePackage = createMessagePackage(mAccelerometerSensorSamples, batteryPercentage);
    mMyWearLogger.logChunkToFile(messagePackage);

    // Sending package in singleton holder
    mDataTransferHolder.getQueueOfMessagePackages().put(messagePackageId, messagePackage);

    Intent sendPackageIntent = new Intent(this, DataProcessingService.class);
    sendPackageIntent.putExtra(MESSAGE_PACKAGE_ID, messagePackageId);
    startService(sendPackageIntent);
}

private MessagePackage createMessagePackage(ArrayList<AccelerometerSampleData> mAccelerometerSensorSamples, float batteryPercentage) {
    MessagePackage messagePackage = new MessagePackage();
    messagePackage.setAccelerometerSamples(mAccelerometerSensorSamples);
    messagePackage.setBatteryPercentage(batteryPercentage);
    return messagePackage;
}

private float getBatteryStatus() {

    IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    Intent batteryStatus = this.registerReceiver(null, ifilter);

    float batteryPercentage;
    if (batteryStatus != null) {
        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        batteryPercentage = level / (float) scale;
    } else {
        Timber.w("failed to retrieve battery percentage");
        batteryPercentage = -1;
    }

    return batteryPercentage;
}

private void addNewEventToPackage(AccelerometerSampleData newEventData) {
    mAccelerometerSensorSamples.add(newEventData);
}

private void updateCurrentValues(AccelerometerSampleData newEventData) {
    mLastEventData = newEventData;
    mCounter++;
}

private void logData(AccelerometerSampleData newEventData, long diff) {
//        if (diff > MAX_ALLOWED_SAMPLES_DIFF_IN_MILLIS
//                || diff < MIN_ALLOWED_SAMPLES_DIFF_IN_MILLIS) {
    Timber.d("new accelerometer event, timestamp: %s, time difference: %s milliseconds",
            newEventData.getTimestamp(), diff);
//        }
}

private long calculateTimeDiff(AccelerometerSampleData newEventData) {

    if (mLastEventData == null) {
        return 0;
    }

    return (newEventData.getTimestamp() - mLastEventData.getTimestamp());

}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

}
4

0 回答 0