Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/201.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Android BluetoothSocket-超时_Java_Android_Multithreading_Bluetooth_Futuretask - Fatal编程技术网

Java Android BluetoothSocket-超时

Java Android BluetoothSocket-超时,java,android,multithreading,bluetooth,futuretask,Java,Android,Multithreading,Bluetooth,Futuretask,我已经编写了一个用于连接外部附件的蓝牙API。 API的设计方式是有一系列阻塞调用,例如getTime,setTime,getVolume,setVolume,等等。 它们的工作方式是创建一个有效负载来发送和调用一个名为sendandereceive()的方法,该方法进行一些准备工作,并最终执行以下操作: byte[] retVal = null; BluetoothSocket socket = getSocket(); // write socket.getOutputStream().wr

我已经编写了一个用于连接外部附件的蓝牙API。 API的设计方式是有一系列阻塞调用,例如
getTime
setTime
getVolume
setVolume
,等等。 它们的工作方式是创建一个有效负载来发送和调用一个名为
sendandereceive()
的方法,该方法进行一些准备工作,并最终执行以下操作:

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;
问题是,有时这个设备会变慢或没有响应,所以我想在这个呼叫上设置一个超时。 我尝试了几种方法将此代码放入线程\未来任务中,并在超时情况下运行它,例如:

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;
}
FutureTask任务=null;
//创建新任务
任务=新的未来任务(
新的可调用(){
@凌驾
公共字节[]调用(){
字节[]retVal=null;
BluetoothSocket=getSocket();
//写
socket.getOutputStream().write(有效负载);
//读取响应
如果(响应预期){
byte[]buffer=新字节[1024];//流的缓冲区存储
int readbytes=socket.getInputStream().read(缓冲区);
retVal=新字节[readbytes];
数组复制(缓冲区,0,返回值,0,读取字节);
}
返回返回;
}
});
//在新线程中启动任务
新线程(任务).start();
//等待执行完成,6秒后超时
字节[]响应;
试一试{
响应=任务get(6L,时间单位秒);
}捕捉(中断异常e){
抛出新的cbt异常(e);
}捕获(执行例外){
抛出新的cbt异常(e);
}捕获(超时异常e){
抛出新的CbtCallTimedOutException(e);
}
返回响应;
}
这种方法的问题是,我不能在call方法中重新抛出异常,因为有些方法抛出异常,我想将其转发回API的客户机,所以我不能使用这种方法

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

为什么不试试像这样的东西呢

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;
}
这里有一种边缘情况,调用
wrappedSendReceive()
的线程可能会由于某种原因而被中断,而不是ReadTask的中断。我想可以在ReadTask中添加一个done位,以允许另一个线程测试读取是否已完成或中断是由其他原因引起的,但我不确定这是否有必要


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

如果要保存,则不能使用将来的方法,因为您希望重新引发异常,但实际上这是可能的

大多数在线示例都使用prototype
public实现了Callable?调用()
但只需将其更改为
公共?call()抛出异常
,一切都会好起来:您将在task.get()调用中获得异常,您可以将其重新发送给调用者

我个人使用Executors在android上处理蓝牙套接字超时:

protected static String readAnswer(...)
throws Exception {
    String timeoutMessage = "timeout";
    ExecutorService executor = Executors.newCachedThreadPool();
    Callable<String> task = new Callable<String>() {
       public String call() throws Exception {
          return readAnswerNoTimeout(...);
       }
    };
    Future<String> future = executor.submit(task);
    try {
       return future.get(SOCKET_TIMEOUT_MS, TimeUnit.MILLISECONDS); 
    } catch (TimeoutException ex) {
        future.cancel(true);
        throw new Exception(timeoutMessage);
    }
}
受保护的静态字符串readAnswer(…)
抛出异常{
字符串timeoutMessage=“timeout”;
ExecutorService executor=Executors.newCachedThreadPool();
可调用任务=新的可调用任务(){
公共字符串调用()引发异常{
返回readAnswerNoTimeout(…);
}
};
未来=执行者提交(任务);
试一试{
返回future.get(套接字超时毫秒,时间单位毫秒);
}捕获(TimeoutException例外){
future.cancel(true);
抛出新异常(超时消息);
}
}

这不是强制每个调用延迟6秒吗?不,如果读取在6秒超时之前完成,读取器线程会向调用wrappedSendReceive()的线程发送一个中断。