Android Service中的数据并发与传感器数据

时间:2017-09-29 07:39:27

标签: java android multithreading concurrency android-service

我的应用程序正在运行Service,它与多传感器腕带保持BLE连接。 Service实现了腕带SDK的一些回调方法,这些方法每秒都会使用新数据调用几次。

我希望将来自不同传感器的这些数据放在相对于其时间戳的同一Observation对象中。所有Observation对象每隔60秒被推送到后端服务器,传感器数据被放在一起以减少发送这些Observation对象的开销。

我现在正在做的是在下面的代码段中。我的问题是while中的observationFetcher - 循环完全阻止了应用程序。是否还有其他方法可以在不使用块while循环的情况下同步这些传感器数据?

    observationFetcher = new Runnable() {
        @Override
        public void run() {
            while (isRecording) {
                if (lastMillis != currentMillis) {
                    Observation obs = sm.getValues();
                    obs.setPropertyAsString("gateway.id", UUID);
                    observations.add(obs);
                    lastMillis = currentMillis;
                }
            }
        }
    };

public void didReceiveGSR(float gsr, double timestamp) {
    long t = System.currentTimeMillis() / 1000;

    sm.setGsrValue(t, gsr);
    currentMillis = t;
}

public void didReceiveIBI(float ibi, double timestamp) {
    sm.setIbiValue(ibi);
}

sm是一个具有synchronized方法的对象,用于将所有传感器数据放在同一秒内。

1 个答案:

答案 0 :(得分:1)

如果我错了,请纠正我,但我没有理由浪费CPU时间无限迭代。当然,我没有看到整个代码,你的API可能不允许你做某事,但我会按照以下方式实现数据处理:

final class Observation {
    private float gsr;
    private float ibi;

    public Observation(float gsr, float ibi) {
        this.gsr = gsr;
        this.ibi = ibi;
    }

    // getters & setters

}

public final class Observations {
    private final ConcurrentHashMap<Long, Observation> observations = new ConcurrentHashMap<>();

    public void insertGsrValue(long timestamp, float gsr) {
        for (;;) {
            Observation observation = observations.get(timestamp);
            if (observation == null) {
                observation = observations.putIfAbsent(timestamp, new Observation(gsr, 0.0f));
                if (observation == null) {
                    return;
                }
            }
            if (observations.replace(timestamp, observation, new Observation(gsr, observation.getIbi()))) {
                return;
            }
        }
    }

    public void insertIbiValue(long timestamp, float ibi) {
        for (;;) {
            Observation observation = observations.get(timestamp);
            if (observation == null) {
                observation = observations.putIfAbsent(timestamp, new Observation(0.0f, ibi));
                if (observation == null) {
                    return;
                }
            }
            if (observations.replace(timestamp, observation, new Observation(observation.getGsr(), ibi))) {
                return;
            }
        }
    }

    public List<Observation> getObservations() {
        return new ArrayList<>(observations.values());
    }

    public void clear() {
        observations.clear();
    }

}

public final class ObservationService extends Service {
    private final Observations observations = new Observations();
    private volatile long currentMillis;
    private HandlerThread handlerThread;
    private Handler handler;

    @Override
    public void onCreate() {
        super.onCreate();
        handlerThread = new HandlerThread("observations_sender_thread");
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                sendData();
                handler.postDelayed(this, TimeUnit.SECONDS.toMillis(60));
            }
        }, TimeUnit.SECONDS.toMillis(60));
    }

    @Override
    public void onDestroy() {
        handlerThread.quit();
    }

    private void sendData() {
        List<Observation> observationList = observations.getObservations();
        observations.clear();
        // send observation list somehow
    }

    public void didReceiveGSR(float gsr, double timestamp) {
        // assuming this is called on a worker thread
        long t = System.currentTimeMillis() / 1000;
        observations.insertGsrValue(t, gsr);
        currentMillis = t;
    }

    public void didReceiveIBI(float ibi, double timestamp) {
        // assuming this is called on a worker thread
        observations.insertIbiValue(currentMillis, ibi);
    }

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

所以这段代码的作用是将传感器中的新值插入到哈希映射中,并每隔60秒将其发送到某个地方。由于并发存在问题,此代码仍然不完美。例如,如果首先得到2个gsr值,然后是一个ibi值,那么我们将丢失第一个gsr值。

无论如何,这段代码应该让你知道如何避免阻塞线程并存储数据并发。

如果您对该代码有任何疑问,请告诉我。