В настоящее время у меня есть рабочий поток ввода-вывода от Android BluetoothChat Example, но у вас проблемы. Мое приложение подключается через 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
возвращает a 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
, будет с благодарностью.
EDIT:
Это новый код потока, который я пробовал, я в настоящее время меняю его на то, что показывает данный ответ и пытается это сделать. Я отправлю это, если он не работает после.
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();
}
}
ИЗМЕНИТЬ 2:
Я попробовал ответить Dheerej
ниже. Я получаю IllegalMonitorStateException
в вызове функции wait()
. Я попытался, как было показано в ответе, а затем попробовал myThread.wait()
вместо Thread.currentThread.wait()
. Я предполагаю, что это исключение выбрасывается, потому что это объект 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