Android BluetoothSocket - 超时 [英] Android BluetoothSocket - Timing out

查看:16
本文介绍了Android BluetoothSocket - 超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个用于连接外部配件的蓝牙 API.API的设计方式是有一堆阻塞调用,例如getTimesetTimegetVolumesetVolume 等这些工作的方式是创建一个有效负载来发送和调用一个名为 sendAndReceive() 的方法,该方法执行一些准备工作并最终执行以下操作:

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;

问题是有时此设备会变慢或无响应,因此我想在此调用上设置超时.我已经尝试了几种将这段代码放在线程未来任务中并超时运行的方法,例如:

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

这种方法的问题是我不能在 call 方法中重新抛出异常,而且由于某些方法抛出异常我想转发回 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!

推荐答案

为什么不试试这样的事情

Why not try something like

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 中添加一个 done 位,以允许其他线程测试读取是否完成或中断是由其他原因引起的,但我不确定这有多大必要.

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.

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

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.

这篇关于Android BluetoothSocket - 超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆