Создание потока, который отменяет вызов InputStream.read (), если прошло время «x»

В настоящее время у меня есть рабочий поток ввода-вывода из примера BluetoothChat от Android, но у вас проблемы. Мое приложение подключается через bluetooth к модулю bluetooth, который, в свою очередь, посылает сигнал на устройство, к которому физически подключен модуль.

Моя программа вызывает read() во входном потоке, и если отправляются данные, программа выполняется без проблем. Однако способ реализации потока не защищает от прерывания соединения. Если модуль физически удален с устройства или если устройство не отправляет никаких сигналов, мой код просто сидит и ждет вызова InputStream.read() .

Мой вызов read() выглядит следующим образом:

 try { Log.i( "1) I/O", "available bits: " + mmInStream.available() ); bytes = mmInStream.read(buffer, 0, length); Log.i( "2) I/O", "available bits: " + mmInStream.available() ); mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } catch (Exception e) { Log.i(TAG, "Catch Statement" ); Message msg = mHandler.obtainMessage(MainMenu.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString( TOAST, "Device has disconnected from the Bluetooth Module." ); msg.setData(bundle); mHandler.sendMessage(msg); Log.e(TAG, "disconnected a", e); connectionLost(); // Start the service over to restart listening mode BluetoothService.this.start(); //break; } 

Когда моя программа действует правильно, оба вызова Log в блоке try возвращают значения 0 для mmInStream.available() . Когда входной поток прерывается, первоначальный вызов Log возвращает 0 , а второй никогда не вызывается. Моя программа затем заканчивается сбой, прежде чем каждый блок catch будет достигнут.

Я искал несколько дней, чтобы исправить это, и нашел множество решений, но они либо не работали, либо я их не понимаю.

1) Использование сканера для InputStream показано ниже. Это не помогало, а также время при чтении.

 Scanner scan = new Scanner(new InputStreamReader(mmInStream)); scan.useDelimiter( "[\\r\\n]+" ); String readIn; try { readIn = scan.next(); scan = null; tempB = readIn.getBytes( Charset.forName( "US-ASCII" ) ); append = "\r\n".getBytes( Charset.forName( "US-ASCII" ) ); for( int i = 0; i < length; i++ ) { if( i == length - 1 ) { buffer[i] = append[1]; } else if ( i == length - 2 ) { buffer[i] = append[0]; } else { buffer[i] = tempB[i]; } } mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } catch (Exception e) { Log.i(TAG, "Catch Statement" ); Message msg = mHandler.obtainMessage(MainMenu.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString( TOAST, "Device has disconnected from the Bluetooth Module." ); msg.setData(bundle); mHandler.sendMessage(msg); Log.e(TAG, "disconnected a", e); connectionLost(); // Start the service over to restart listening mode BluetoothService.this.start(); //break; } 

2) Я попробовал запустить Thread, который отменил бы вызов read через X раз, но он не сработает правильно:

 public void run(int length) throws IOException { buffer = new byte[1024]; length1 = length; Thread myThread = new Thread(new Runnable() { public void run() { try { bytes = mmInStream.read( buffer, 0, length1 ); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); synchronized (myThread) { myThread.start(); try { myThread.wait(500); if(myThread.isAlive()) { mmInStream.close(); Log.i( "InStream", "Timeout exceeded!"); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { myThread.run(); mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } catch (IOException e) { Message msg = mHandler.obtainMessage(MainMenu.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString( TOAST, "Device has disconnected from the Bluetooth Module." ); msg.setData(bundle); mHandler.sendMessage(msg); connectionLost(); BluetoothService.this.start(); } 

После того, как эти два варианта не сработали, я пытался заглянуть в Java NIO или AsyncTask , но все это похоже на слишком много материала, чтобы добавить для распознавания тайм-аута ввода-вывода. Я также видел, что некоторые Sockets поддерживают функцию тайм-аута с использованием .setSoTimeout() , однако это BluetoothSocket и из того, что я нашел, они не поддерживают эту функцию.

Поскольку нет класса I/O который поддерживает метод read() который принимает длину тайм-аута в качестве параметра или таймаута вообще, мне кажется, что добавление Thread было бы самой простой реализацией. Это неправильно? Любая информация о том, что я делаю неправильно с вышеуказанными методами, или о том, как включить Java NIO / AsyncTask будет с благодарностью.

РЕДАКТИРОВАТЬ:

Это новый код потока, который я пробовал, я в настоящее время меняю его на то, что показывает данный ответ и пытается это сделать. Я опубликую это, если это не сработает.

 Thread myThread = new Thread(new Runnable() { public void run() { try { bytes = mmInStream.read( buffer, 0, length1 ); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); synchronized (myThread) { try { myThread.wait(6000); Log.i( "InStream", "After wait" ); if(myThread.isAlive()) { Log.i( "InStream", "Timeout exceeded2!"); myThread.interrupt(); Log.i( "InStream", "Timeout exceeded!"); } else { myThread.interrupt(); } } catch (InterruptedException e) { // TODO Auto-generated catch block Log.i( "InStream", "Exception Caught" ); e.printStackTrace(); } } 

EDIT 2:

Я попробовал ответ, который Dheerej дал ниже. Я получаю IllegalMonitorStateException при IllegalMonitorStateException функции wait() . Я попытался, как было показано в ответе, а затем попробовал myThread.wait() вместо Thread.currentThread.wait() . Я предполагаю, что это исключение выбрасывается, потому что это объект myThread создается и myThread в другом потоке. Во всяком случае, приведенный ниже код почти идентичен Dheerej's .

  int length1 = length; Thread myThread = new Thread(new Runnable() { public void run() { buffer = new byte[1024]; try { bytes = mmInStream.read(buffer, 0, length1); } catch (IOException e) { e.printStackTrace(); } mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } }); myThread.start(); try { //Thread.currentThread().wait(500); myThread.wait( 1000 ); // Line 533 } catch (InterruptedException e) { e.printStackTrace(); //Log.i(TAG, "Catch Statement" ); Message msg = mHandler.obtainMessage(MainMenu.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString( TOAST, "Device has disconnected from the Bluetooth Module." ); msg.setData(bundle); mHandler.sendMessage(msg); Log.e(TAG, "disconnected a", e); connectionLost(); // Start the service over to restart listening mode BluetoothService.this.start(); } if (myThread.isAlive()) { mmInStream.close(); // Alternatively try: myThread.interrupt() } 

Это результирующий LogCat. Ошибка говорит, что она начинается в строке 533, которая является вызовом wait() выше:

 12-28 17:44:18.765: D/BLZ20_WRAPPER(3242): blz20_wrp_poll: return 1 12-28 17:44:18.765: D/BLZ20_WRAPPER(3242): blz20_wrp_write: wrote 3 bytes out of 3 on fd 62 12-28 17:44:18.769: W/NATIVE CODE(3242): -4) baud9600=1, goodbaud=1 12-28 17:44:18.769: D/AndroidRuntime(3242): Shutting down VM 12-28 17:44:18.769: W/dalvikvm(3242): threadid=1: thread exiting with uncaught exception (group=0x40015578) 12-28 17:44:18.773: E/AndroidRuntime(3242): FATAL EXCEPTION: main 12-28 17:44:18.773: E/AndroidRuntime(3242): java.lang.IllegalMonitorStateException: object not locked by thread before wait() 12-28 17:44:18.773: E/AndroidRuntime(3242): at java.lang.Object.wait(Native Method) 12-28 17:44:18.773: E/AndroidRuntime(3242): at java.lang.Object.wait(Object.java:395) 12-28 17:44:18.773: E/AndroidRuntime(3242): at my.eti.commander.BluetoothService$ConnectedThread.run(BluetoothService.java:533) 12-28 17:44:18.773: E/AndroidRuntime(3242): at my.eti.commander.BluetoothService.read(BluetoothService.java:326) 12-28 17:44:18.773: E/AndroidRuntime(3242): at my.eti.commander.BluetoothService.changeitJava(BluetoothService.java:669) 12-28 17:44:18.773: E/AndroidRuntime(3242): at my.eti.commander.RelayAPIModel$NativeCalls.changeItJavaWrapper(RelayAPIModel.java:490) 12-28 17:44:18.773: E/AndroidRuntime(3242): at my.eti.commander.RelayAPIModel$NativeCalls.InitRelayJava(Native Method) 12-28 17:44:18.773: E/AndroidRuntime(3242): at my.eti.commander.MainMenu$1.handleMessage(MainMenu.java:547) 12-28 17:44:18.773: E/AndroidRuntime(3242): at android.os.Handler.dispatchMessage(Handler.java:99) 12-28 17:44:18.773: E/AndroidRuntime(3242): at android.os.Looper.loop(Looper.java:130) 12-28 17:44:18.773: E/AndroidRuntime(3242): at android.app.ActivityThread.main(ActivityThread.java:3687) 12-28 17:44:18.773: E/AndroidRuntime(3242): at java.lang.reflect.Method.invokeNative(Native Method) 12-28 17:44:18.773: E/AndroidRuntime(3242): at java.lang.reflect.Method.invoke(Method.java:507) 12-28 17:44:18.773: E/AndroidRuntime(3242): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842) 12-28 17:44:18.773: E/AndroidRuntime(3242): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600) 12-28 17:44:18.773: E/AndroidRuntime(3242): at dalvik.system.NativeStart.main(Native Method) 12-28 17:44:18.781: D/BLZ20_ASOCKWRP(3242): asocket_read 12-28 17:44:18.781: I/BLZ20_WRAPPER(3242): blz20_wrp_poll: nfds 2, timeout -1 ms 12-28 17:44:18.890: D/BLZ20_WRAPPER(3242): blz20_wrp_poll: transp poll : (fd 62) returned r_ev [POLLIN ] (0x1) 12-28 17:44:18.890: D/BLZ20_WRAPPER(3242): blz20_wrp_poll: return 1 12-28 17:44:18.890: D/BLZ20_WRAPPER(3242): blz20_wrp_read: read 5 bytes out of 5 on fd 62 

    Попробуйте это сначала:

     try { int available = 0; while (true) { int available = mmInStream.available(); if (available > 0) { break; } Thread.sleep(1); // here you can optionally check elapsed time, and time out } Log.i( "1) I/O", "available bits: " + available ); bytes = mmInStream.read(buffer, 0, length); Log.i( "2) I/O", "available bits: " + mmInStream.available() ); mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer).sendToTarget(); } catch (Exception e) { ... } 

    В исходном коде вы вызываете available() перед read() , как правило, нет данных, ожидающих чтения. Затем вы вызываете read() , который блокирует и ждет данных, затем читает все это. Затем вы снова вызываете available() и еще раз нет данных, потому что все это было прочитано 🙂 Лучше: спать, пока не будет available() возвращает ненулевое значение, затем читает. Однако это может не сработать, потому что available() всегда разрешено возвращать 0 (даже если данные действительно доступны).

    Если вышеуказанное не работает, попробуйте технику из этого вопроса: возможно ли читать из InputStream с таймаутом?

     Callable<Integer> readTask = new Callable<Integer>() { @Override public Integer call() throws Exception { return mmInStream.read(buffer, 0, length); } } try { Future<Integer> future = executor.submit(readTask); bytes = future.get(100, TimeUnit.MILLISECONDS); mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer).sendToTarget(); } catch (TimeoutException e) { // deal with timeout in the read call } catch (Exception e) { ... } 

    Наконец, в документах BluetoothSocket говорится, что вы можете закрыть сокет из любого потока и вступить в силу немедленно. Таким образом, вы можете просто иметь контрольный поток, и если вызов чтения не удался, вызовите функцию close() в сокете, что приведет к возврату заблокированного read() с ошибкой. Это было то, что предложил Dheeraj выше, но вам нужно только вызвать close() когда другой поток застрял (из-за ошибки сети / потеря связи / etc): в противном случае просто проверяйте его ход один раз в то время, но не закрывайте Пока ваше чтение не зашло слишком долго.

    Это, безусловно, выглядит как нехватка тайм-аутов (и невозможность прерывания заблокированного чтения () извне) долгое время являлась главной продолжающейся болью в Java.

    Смотрите также:

    Можно ли читать из InputStream с таймаутом? (Использует Callable / Future )

    Могу ли я установить тайм-аут для функции чтения () InputStream? (Использует Socket.setSoTimeout() )

    Как убить вызов BufferedInputStream .read () (использует InterruptibleChannel )

    Как остановить поток, ожидающий операции блокировки чтения в Java?

    Попробуйте этот код, который расширяется в моем комментарии выше:

     public void run(final int length) { Thread myThread = new Thread(new Runnable() { public void run() { buffer = new byte[1024]; try { bytes = mmInStream.read(buffer, 0, length); } catch (IOException e) { e.printStackTrace(); } mHandler.obtainMessage(MainMenu.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } }); myThread.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (myThread.isAlive()) { mmInStream.close(); // Alternatively try: myThread.interrupt() } }