Android蓝牙套装 – 计时

我写了一个用于连接外部附件的蓝牙API。 设计API的方式是有一堆阻塞调用,如getTimesetTimegetVolumesetVolume等。这些工作方式是他们创建一个有效负载来发送和调用一个名为sendAndReceive()的方法,一些准备工作,最终做到以下几点:

 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; 

问题是,有时这个设备会变慢或没有响应,所以我想对这个调用设置超时。 我已经尝试了几种将此代码放在线程\ future任务中并使用超时运行它的方法,例如:

 FutureTask theTask = null; // create new task theTask = new FutureTask( new Callable() { @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; } 

这种方法的问题在于我无法在调用方法中重新抛出exception,并且由于某些方法抛出了我想要转发回API客户端的exception,因此我无法使用此方法。

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

为什么不尝试类似的东西

 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; } 

这里有一个边缘情况,Thread调用wrappedSendAndReceive()可能由于某些原因而被中断,而不是来自ReadTask的中断。 我想可以在ReadTask中添加一个完成位,以允许另一个线程测试读取完成或中断是否由其他内容引起,但我不确定这是多么必要。

另外需要注意的是,此代码确实包含数据丢失的可能性。 如果6秒到期并且已经读取了一些数据,则最终将被丢弃。 如果你想解决这个问题,你需要在ReadTask.run()中一次读取一个字节,然后适当地捕获InterruptedException。 这显然需要对现有代码进行一些小的修改以保持计数器并在接收到中断时适当地调整读缓冲区的大小。

你正在保存你不能使用Future <>方法,因为你想重新抛出异常,但事实上这是可能的。

大多数在线示例都实现Callable与原型public ? call() public ? call()但只是将其更改为public ? call() throws Exception public ? call() throws Exception ,一切都会好的:你将在theTask.get()调用中得到exception,你可以将它重新抛出给调用者。

我个人使用Executors完全用于android上的蓝牙套接字超时处理:

 protected static String readAnswer(...) throws Exception { String timeoutMessage = "timeout"; ExecutorService executor = Executors.newCachedThreadPool(); Callable task = new Callable() { public String call() throws Exception { return readAnswerNoTimeout(...); } }; Future future = executor.submit(task); try { return future.get(SOCKET_TIMEOUT_MS, TimeUnit.MILLISECONDS); } catch (TimeoutException ex) { future.cancel(true); throw new Exception(timeoutMessage); } }