Вопрос

Я написал API Bluetooth для подключения с внешним аксессуаром. Способ, которым разработан 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;
}

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

Здесь есть крайний случай, который вызывает поток wrappedSendAndReceive() может быть прерван по какой -то причине, кроме прерывания от ReadTask. Я полагаю, что в чтение может быть добавлен проведенный бит, чтобы другой поток проверил, было ли чтение закончено или прерывание было вызвано чем -то другим, но я не уверен, насколько это необходимо.

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

Другие советы

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

Большинство примеров в Интернете реализуют Callable с прототипом public ? call() Но просто измените это на public ? call() throws Exception И все будет в порядке: вы получите исключение в Thetsk.get () Call, и вы сможете переосмыслить его для абонентов.

Я лично использовал исполнителей именно для обработки тайм -аута 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);
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top