![](/img/trans.png)
[英]Thread to catch a BluetoothSocket InputStream.read() timeout
[英]Making a thread which cancels a InputStream.read() call if 'x' time has passed
我目前有一個來自Android的BluetoothChat示例的工作I / O流,但遇到了問題。 我的應用程序通過藍牙連接到藍牙模塊,藍牙模塊又向模塊物理連接的設備發送信號。
我的程序在輸入流上調用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;
}
當我的程序正常運行時, try
塊中的兩個Log
調用都為mmInStream.available()
返回值0
。 當輸入流被中斷時,初始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)我已經嘗試運行一個線程,它會在X時間后取消read
調用,但它無法正常工作:
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
,但所有這些似乎都是為了識別I / O超時而添加的東西。 我也看到一些Sockets
支持使用.setSoTimeout()
的超時功能,但這是一個BluetoothSocket
.setSoTimeout()
,我發現它們不支持此功能。
由於沒有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();
}
}
編輯2:
我已經嘗試過Dheerej
在下面給出的答案。 我在wait()
函數調用上得到IllegalMonitorStateException
。 我嘗試了,因為它在答案中顯示,然后也嘗試了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
先試試這個:
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) {
...
}
在原始代碼中,在read()
available()
之前調用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
文檔說您可以從任何線程關閉套接字並立即生效。 所以你可以簡單地有一個看門狗線程,如果讀取調用沒有成功,則調用socket上的close()
,這會導致阻塞的read()
返回錯誤。 這就是Dheeraj上面提到的,但是你只需要在另一個線程被卡住時調用close()
(由於網絡錯誤/連接丟失/等):否則只是偶爾檢查一下它的進度但是不要關閉只要你的閱讀時間不長。
這當然看起來缺乏超時(和()從外部中斷阻塞讀是不可能的),已經在Java中很長一段時間內正在進行的主要痛點。
也可以看看:
是否可以從具有超時的InputStream讀取? (使用Callable
/ Future
)
我可以為InputStream的read()函數設置超時嗎? (使用Socket.setSoTimeout()
)
如何殺死BufferedInputStream .read()調用 (使用InterruptibleChannel
)
試試這個代碼,它擴展了我上面的評論:
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()
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.