Android BluetoothSocket - Timing Out
-
27-10-2019 - |
Вопрос
Я написал 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);
}
}