Android BluetoothSocket - тайм-аут - PullRequest
4 голосов
/ 14 июня 2011

Я написал Bluetooth API для подключения к внешнему аксессуару.Способ разработки API заключается в том, что существует множество блокирующих вызовов, таких как getTime, setTime, getVolume, setVolume и т. Д. Способ, которым они работают, заключается в том, что они создают полезную нагрузку для отправки и вызоваметод с именем 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;

Проблема заключается в том, что иногда это устройство работает медленно или не отвечает, поэтому я хотел бы установить тайм-аут на этот вызов,Я пробовал несколько способов поместить этот код в задачу потока \ будущего и запустить его с таймаутом, например:

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, я не могу использовать эту методологию.

Можете ли вы порекомендовать какой-нибудь другой вариант?Спасибо!

Ответы [ 2 ]

2 голосов
/ 21 сентября 2012

Вы сохраняете, вы не можете использовать метод Future <>, потому что хотите повторно вызвать исключение, но на самом деле это возможно.

Большинство онлайн-примеров реализуют Callable с прототипом public ? call(), но просто измените его на public ? call() throws Exception, и все будет хорошо: вы получите исключение в вызове theTask.get () и можете переадресовать его вызывающим .

Я лично использовал Executors именно для обработки таймаута bluetooth-сокета на 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);
    }
}
1 голос
/ 23 июня 2011

Почему бы не попробовать что-то вроде

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, чтобы позволить другому потоку проверить, завершено ли чтение или вызвано ли прерывание чем-то другим, но я не уверен, насколько это необходимо.

Ещеобратите внимание, что этот код содержит возможность потери данных.Если истечет 6 секунд и будет прочитано некоторое количество данных, это в конечном итоге будет отброшено.Если вы хотите обойти это, вам нужно будет читать по одному байту за раз в ReadTask.run (), а затем соответствующим образом перехватывать InterruptedException.Это, очевидно, требует небольшой доработки существующего кода, чтобы сохранить счетчик и соответствующим образом изменить размер буфера чтения при получении прерывания.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...