Android BluetoothSocket-タイミングアウト
-
27-10-2019 - |
質問
外部アクセサリに接続するための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;
問題は、このデバイスが遅くなったり、反応したりしないことがあるので、この呼び出しにタイムアウトを付けたいと思います。このコードをスレッド futureタスクに入れて、タイムアウトで実行する方法をいくつか試しました。たとえば、:
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からの割り込み以外に何らかの理由で中断される可能性があります。他のスレッドが読み取りが終了したか、割り込みが他の何かによって引き起こされたかどうかをテストできるように、readtaskに少し追加することができると思いますが、これがどれほど必要かはわかりません。
さらなる注意は、このコードにはデータ損失の可能性が含まれていることです。 6秒の有効期限が切れ、ある程度のデータが読まれた場合、これは破棄されることになります。これを回避したい場合は、readtask.run()で一度に1つのバイトを読んでから、中断exceptionを適切にキャッチする必要があります。これには、明らかに、カウンターを保持し、割り込みを受信したときに読み取りバッファーを適切にサイズ変更するために、既存のコードを少しの再作品が必要です。
他のヒント
あなたは例外を再スローしたいので、未来<>メソッドを使用することはできませんが、実際にはこれが可能です。
オンラインのほとんどの例は、プロトタイプで呼び出し可能な実装を実装しています public ? call()
しかし、それを変更するだけです public ? call() throws Exception
すべてが問題ありません。Thetask.get()電話で例外を取得し、発信者にそれを蘇らせることができます。
AndroidでのBluetooth Socket Timeoutハンドリングに正確に執行者を個人的に使用しました。
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);
}
}