Android BluetoothSocket - Timing

I wrote a Bluetooth API to connect to an external accessory. The way to design an API is that there are many blocking calls like getTime , setTime , getVolume , setVolume , etc. How they work, they create a payload for sending and calling a method called sendAndReceive() , which does some preparatory work, and ultimately does the following:

 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; 

The problem is that sometimes this device becomes slow or unresponsive, so I want to put a timeout on this call. I tried several methods to put this code in the thread \ future task and run it with a timeout, for example:

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

The problem with this approach is that I cannot repeatedly exclude exceptions from the call method, and since some of the methods throw exceptions that I want to send back to the API client, I cannot use this methodology.

Can you recommend another alternative? Thanks!

+4
source share
2 answers

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 a marginal case here that a Thread wrappedSendAndReceive() call might be interrupted for some reason other than interrupting from ReadTask. I suggest that a finished bit can be added to the ReadTask to allow another thread to check whether the reading has finished or if the interrupt was caused by something else, but I'm not sure how necessary it is.

It should further be noted that this code does indeed contain the possibility of data loss. If 6 seconds elapse and a certain amount of data has been read, it will end up being discarded. If you want to get around this, you will need to read one byte at a time in ReadTask.run (), and then catch an InterruptedException accordingly. This, obviously, requires a small alteration of the existing code in order to save the counter and accordingly change the size of the read buffer when an interrupt is received.

+1
source

You save, you cannot use the Future <> method, because you want to throw an exception again, but actually it is possible.

Most online examples implement Callable with a public ? call() prototype public ? call() public ? call() , but just change it to public ? call() throws Exception public ? call() throws Exception , and everything will be fine: you will get an exception in the call to theTask.get (), and you can return it to the callers.

I personally used Executors specifically to handle the bluetooth socket timeout 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); } } 
+2
source

All Articles