Question

J'ai écrit une API Bluetooth pour la connexion avec un accessoire externe. La façon dont l'API est conçu est qu'il ya un tas de blocage des appels tels que getTime, setTime, getVolume, setVolume, etc. La façon dont ces travaux est qu'ils créent une charge utile pour envoyer et appeler une méthode appelée sendAndReceive() qui fait un travail de préparation et fait finalement ce qui suit:

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;

Le problème est que, parfois, cet appareil devient lent ou non réactif, donc je voudrais mettre un délai d'attente sur cet appel. J'ai essayé plusieurs méthodes de mettre ce code dans un thread \ tâche future et en cours d'exécution avec un délai d'attente, par exemple:

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

Le problème avec cette approche est que je ne peux pas exception re-jeter dans la méthode d'appel et depuis certaines des méthodes lancer des exceptions que je veux transmettre de nouveau au client de l'API, je ne peux pas utiliser cette méthode.

Pouvez-vous recommander une autre alternative? Merci!

Était-ce utile?

La solution

Pourquoi ne pas essayer quelque chose comme

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

Il y a un cas limite ici que le thread appelant wrappedSendAndReceive() peut être interrompue pour une raison autre que l'interruption du ReadTask. Je suppose un peu fini pourrait être ajouté à la ReadTask pour permettre à l'autre fil à tester si la lecture terminée ou l'interruption a été provoquée par autre chose, mais je ne suis pas sûr nécessaire c'est.

Une autre note est que ce code ne contient la possibilité de perte de données. Si les 6 secondes arrive à expiration et une certaine quantité de données a été lu cela va finir par être mis au rebut. Si vous voulez travailler autour de cela, vous aurez besoin de lire un octet à la fois dans ReadTask.run () et puis prendre de manière appropriée le InterruptedException. Cela nécessite évidemment un peu réusinage du code existant pour maintenir un compteur et redimensionner de façon appropriée la mémoire tampon de lecture lorsque l'interruption est reçue.

Autres conseils

Vous enregistrez vous ne pouvez pas utiliser la méthode Future <> parce que vous voulez à nouveau jeter l'exception mais en fait cela est possible.

La plupart des exemples font en ligne avec le mettre en œuvre appelable public ? call() prototype mais juste changer à public ? call() throws Exception et tout ira bien. Vous aurez l'exception dans le theTask.get () appel et vous pouvez réémettre à appelants

J'ai personnellement utilisé Huissiers exactement Bluetooth socket manipulation délai d'attente sur 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);
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top