安卓的BluetoothSocket - 定时出BluetoothSocket

2023-09-05 11:26:26 作者:听命老婆°

我已经写了蓝牙API,用于与外部附件的连接。 的方式,该API的设计是,有一帮阻塞调用,如的getTime 的setTime , getVolume , setVolume 等。 在这部分的工作是他们创造一个有效载荷发送和调用一个方法称为 sendAndReceive(),做一些preP的工作,并最终执行以下操作:

I have written a Bluetooth API for connecting with an external accessory. The way that the API is designed is that there are a bunch of blocking calls such as getTime, setTime, getVolume, setVolume, etc. The way these work is that they create a payload to send and call a method called sendAndReceive() which does some prep work and eventually does the following:

byte[] retVal = null;
BluetoothSocket socket = getSocket();
// write
socket.getOutputStream().write(payload);
// read response
if(responseExpected){
    byte[] buffer = new byte[1024]; // buffer store for the stream
    int readbytes = socket.getInputStream().read(buffer);
    retVal = new byte[readbytes];
    System.arraycopy(buffer, 0, retVal, 0, readbytes);
}
return retVal;

问题是,有时这种装置变得缓慢或无响应,所以我想提出一个超时的这一呼吁。 我试图把这个code在一个线程中\未来的任务和超时运行它,例如几种方法:

The problem is that sometimes this device becomes slow or non-responsive so I would like to put a timeout on this call. I have tried several methods of putting this code in a thread\future task and running it with a timeout, for example:

FutureTask<byte[]> theTask = null;
// create new task
theTask = new FutureTask<byte[]>(
        new Callable<byte[]>() {

            @Override
            public byte[] call() {
                byte[] retVal = null;
                BluetoothSocket socket = getSocket();
                // write
                socket.getOutputStream().write(payload);
                // read response
                if(responseExpected){
                    byte[] buffer = new byte[1024]; // buffer store for the stream
                    int readbytes = socket.getInputStream().read(buffer);
                    retVal = new byte[readbytes];
                    System.arraycopy(buffer, 0, retVal, 0, readbytes);
                }
                return retVal;
            }
        });

// start task in a new thread
new Thread(theTask).start();

// wait for the execution to finish, timeout after 6 secs
byte[] response;
try {
    response = theTask.get(6L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    throw new CbtException(e);
} catch (ExecutionException e) {
    throw new CbtException(e);
} catch (TimeoutException e) {
    throw new CbtCallTimedOutException(e);
}
    return response;
}

使用这种方法的问题是,我不能重新抛出异常的调用方法,因为一些方法抛出异常我想转发回API的客户,我不能用这种方法。

The problem with this approach is that I can't re-throw exceptions in the call method and since some of the methods throw exceptions I want to forward back to the API's client I can't use this methodology.

你能推荐一些其他的选择吗? 谢谢!

Can you recommend some other alternative? Thanks!

推荐答案

为什么不尝试像

public class ReadTask extends Thread {
  private byte[] mResultBuffer;
  private Exception mCaught;
  private Thread mWatcher;
  public ReadTask(Thread watcher) {
    mWatcher = watcher;
  }

  public void run() {
    try {
      mResultBuffer = sendAndReceive();
    } catch (Exception e) {
      mCaught = e;
    }
    mWatcher.interrupt();
  }
  public Exception getCaughtException() {
    return mCaught;
  }
  public byte[] getResults() {
    return mResultBuffer;
  }
}

public byte[] wrappedSendAndReceive() {
  byte[] data = new byte[1024];
  ReadTask worker = new ReadTask(data, Thread.currentThread());

  try {
    worker.start();
    Thread.sleep(6000);
  } catch (InterruptedException e) {
    // either the read completed, or we were interrupted for another reason
    if (worker.getCaughtException() != null) {
      throw worker.getCaughtException();
    }
  }

  // try to interrupt the reader
  worker.interrupt();
  return worker.getResults;
}

有一个边缘的情况下,这里说的线程调用 wrappedSendAndReceive()可能会因为某些原因不是从ReadTask中断等中断。我想一个完成位可以被添加到ReadTask让其他线程来测试读取完成或中断是由其他东西造成的,但我不知道如何必要的,这是。

There is an edge case here that the Thread calling wrappedSendAndReceive() may get interrupted for some reason other than the interrupt from the ReadTask. I suppose a done bit could be added to the ReadTask to allow the other thread to test if the read finished or the interrupt was caused by something else, but I'm not sure how necessary this is.

还有一个值得注意的是,这个code确实包含了可能的数据丢失。如果6秒期满和数据的一定量的已被读出,这将最终被丢弃。如果你想解决这个问题,你需要读取一个字节在ReadTask.run()一段时间,然后适当地捕捉InterruptedException异常。这显然​​需要对现有的code稍微返工保留一个计数器并适当地调整接收到中断时,读取缓冲区。

A further note is that this code does contain the possibility for data loss. If the 6 seconds expires and some amount of data has been read this will end up being discarded. If you wanted to work around this, you'd need to read one byte at a time in ReadTask.run() and then appropriately catch the InterruptedException. This obviously requires a little rework of the existing code to keep a counter and appropriately resize the read buffer when the interrupt is received.

 
精彩推荐
图片推荐