我正在后台线程上读取蓝牙数据流,并将该数据传递到UiThread,以便通过Message Handler进行处理和显示。可以找到有关该过程的更详细描述here。
目前我正在将InputStream读取到byte[]
并使用Handler.obtainMessage()来获取带有字节数组作为对象争论的消息。
我遇到了一个problem数据包会覆盖彼此,因为数组没有跨线程锁定,并且在UiThread可以处理之前在后台被覆盖。显而易见的解决方案是将数组复制到新数组并传递新对象。
问题是这在Android上非常昂贵,我将获得相当连续的数据流。有没有更好的方法将此数据传递给主UiThread? 也许通过同步字节数组或更高效的内存复制方式?
答案 0 :(得分:5)
在Android中执行此操作的典型方法是拥有一个已分配对象池。上课:
class DataBuffer {
DataBuffer next;
byte[] data;
}
你可以像这样管理它们:
final Object mLock = new Object();
DataBuffer mPool;
DataBuffer obtain() {
synchronized (mLock) {
if (mPool != null) {
DataBuffer res = mPool;
mPool = res.next;
return res;
}
return new DataBuffer();
}
}
void recycle(DataBuffer buffer) {
synchronized (mLock) {
buffer.next = mPool;
mPool = buffer;
}
}
答案 1 :(得分:1)
数据进入与消耗的速度有多快?
为什么不分配第二个数组,一个用于读取一个用于写入,根据需要进行切换。
当流完成写入阵列1时,释放其互斥锁并等待阵列2变为可用。然后你可以通过你的处理程序将数组1传递给UI线程,这将获得该数组的锁定。通过像这样切换数组,您可以确保数据不会丢失,因为通过互斥来控制每个数据。
如果您的数据进入的速度快于读取数据,则可能会出现问题。 (蓝牙数据蒸汽是否可以阻止并等待?)
答案 2 :(得分:1)
听起来你想要的是你可以检查使用的字节数组池,并在完成时检入。实现可能看起来像
public class BytePool {
private class PoolObject {
public byte[] buffer = byte[1024];
public boolean available = false;
}
ArrayList<PoolObject> pool = new ArrayList<PoolObject>();
public synchronized byte[] checkOut() {
boolean found = false;
for (PoolObject obj : pool) {
if (obj.available) {
obj.available = false;
return obj.buffer;
}
}
PoolObject newObj = new PoolObject();
pool.add(newObj);
return newObj.buffer;
}
public synchronized void checkIn(byte[] finished) {
for (PoolObject obj : pool) {
if (obj.buffer == finished) {
obj.available = true;
}
}
}
}
免责声明:我实际上没有测试过甚至编译过这段代码,只是为了传达基本的想法。
有了这个,你希望在任何给定的时间分配最小数量的byte []对象。理想情况下,您还希望添加一些代码以减少池大小,如果由于某种原因它扩展了一堆,现在所需的池大小更小。
我个人认为,在明确是否需要微观优化之前,你可能会担心微观优化。 (或者您可能已经分析过您的应用程序并知道您需要它。)
更新:Hackbod的解决方案实际上比我建议的解决方案更有效,尽管它仍然可能因池中有太多对象而受到影响。
答案 3 :(得分:0)
不知道android,但通常将这些数据读入动态分配的缓冲区,在消息中发布其引用并立即分配一个新数据,为下一批数据做好准备。这消除了复制并确保两个线程始终在不同的数据上运行。
RGDS, 马丁