Android BluetoothSocket - Timing out

前端 未结 2 1839
逝去的感伤
逝去的感伤 2021-01-01 04:24

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<

相关标签:
2条回答
  • 2021-01-01 05:01

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

    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.

    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.

    0 讨论(0)
  • 2021-01-01 05:12

    You're saving you can't use the Future<> method because you want to re-throw the exception but in fact this is possible.

    Most examples online do implement Callable with the prototype public ? call() but just change it to public ? call() throws Exception and all will be fine: you'll get the exception in the theTask.get() call and you can rethrow it to callers.

    I have personally used Executors exactly for bluetooth socket timeout handling on 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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题