简体   繁体   English

如果'x'时间已经过去,则创建一个取消InputStream.read()调用的线程

[英]Making a thread which cancels a InputStream.read() call if 'x' time has passed

I currently have a working I/O stream from Android's BluetoothChat Example , but have run into problems. 我目前有一个来自Android的BluetoothChat示例的工作I / O流,但遇到了问题。 My application connects via bluetooth to a bluetooth module, which in turn sends a signal to a device the module is physically attached to. 我的应用程序通过蓝牙连接到蓝牙模块,蓝牙模块又向模块物理连接的设备发送信号。

My program calls read() on an input stream, and if there is data being sent the program executes smoothly with no problems. 我的程序在输入流上调用read() ,如果有数据被发送,程序可以顺利执行而没有任何问题。 However, the way the stream is implemented there is no protection against an interrupted connection. 但是,实现流的方式不能防止中断连接。 If the module is physically removed from the device, or if the device doesn't send any signals back, my code simply sits and waits at the InputStream.read() call. 如果从设备中物理移除模块,或者设备没有发回任何信号,我的代码就会坐下来等待InputStream.read()调用。

My read() call looks like this: 我的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;
}

When my program acts correctly, both of the Log calls in the try block return values of 0 for mmInStream.available() . 当我的程序正常运行时, try块中的两个Log调用都为mmInStream.available()返回值0 When the input stream is interrupted, the initial Log call returns a 0 , and the second is never called. 当输入流被中断时,初始Log调用返回0 ,而第二个调用永远不会被调用。 My program then ends up crashing before the catch block is every reached. 然后我的程序在每次到达catch块之前都会崩溃。

I have been looking for several days now to fix this, and have found numerous solutions, but they have either not worked, or I do not understand them. 我一直在寻找几天来解决这个问题,并找到了许多解决方案,但它们要么没有用,要么我不理解它们。

1) Using a scanner for the InputStream is shown below. 1)使用扫描仪输入InputStream如下所示。 This provided no help and also timed out while reading. 这没有提供帮助,也在阅读时超时。

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) I have tried running a Thread which would cancel the read call after X amount of time, but it would not work correctly: 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();
   }

After those two options didn't work, I have been trying to look into Java NIO or AsyncTask , but all of this seems like way too much stuff to add for recognizing an I/O timeout. 在这两个选项不起作用之后,我一直在尝试研究Java NIOAsyncTask ,但所有这些似乎都是为了识别I / O超时而添加的东西。 I have also seen that some Sockets support a timeout feature using .setSoTimeout() , however this is a BluetoothSocket and from what I've found they do not support this feature. 我也看到一些Sockets支持使用.setSoTimeout()的超时功能,但这是一个BluetoothSocket .setSoTimeout() ,我发现它们不支持此功能。

Since there is no I/O class which supports a read() method that takes a timeout length as a parameter, or timeout at all, it seems to me that adding a Thread would be the simplest implementation. 由于没有I/O类支持将超时长度作为参数的read()方法,或者根本没有超时,因此在我看来,添加一个Thread将是最简单的实现。 Is this wrong? 这是错的吗? Any information on what I'm doing wrong with the above methods, or how to incorporate Java NIO / AsyncTask would be greatly appreciated. 任何有关我上述方法出错的信息,或者如何合并Java NIO / AsyncTask都将非常感激。

EDIT: 编辑:

This is the new thread code I tried, I am currently changing it to what the given answer shows and trying that. 这是我尝试的新线程代码,我目前正在将其更改为给定答案显示并尝试该操作。 I will post that if it doesn't work after. 如果以后它不起作用我会张贴。

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: 编辑2:

I have tried the answer Dheerej has given below. 我已经尝试过Dheerej在下面给出的答案。 I get an IllegalMonitorStateException on the wait() function call. 我在wait()函数调用上得到IllegalMonitorStateException I tried as it was shown in the answer, then also tried myThread.wait() instead of Thread.currentThread.wait() . 我尝试了,因为它在答案中显示,然后也尝试了myThread.wait()而不是Thread.currentThread.wait() I'm assuming this exception is being thrown because this is myThread object is being created and ran within another thread. 我假设抛出此异常,因为这是myThread对象正在创建并在另一个线程中运行。 Anyway, the code below is almost identical to Dheerej's answer. 无论如何,下面的代码几乎与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()
            }

This is the resulting LogCat. 这是生成的LogCat。 The error says it starts in line 533, which is the wait() call above: 错误说它从第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 this first: 先试试这个:

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) {
    ...
}

In your original code, you call available() before read() , typically there is no data waiting to be read. 在原始代码中,在read() available()之前调用available() read() ,通常没有数据等待读取。 Then you call read() , which blocks and waits for data, then reads all of it. 然后你调用read() ,它阻塞并等待数据,然后读取所有数据。 Then you call available() again and once again there is no data, because it has all been read :) Better: sleep until available() returns nonzero, then read. 然后再次调用available()并再次没有数据,因为它已全部被读取:)更好:睡眠直到available()返回非零,然后读取。 However, this may not work, because available() is always allowed to return 0 (even if data is actually available). 但是,这可能不起作用,因为始终允许available()返回0(即使数据实际可用)。

If the above doesn't work, try the technique from this question: Is it possible to read from a InputStream with a timeout? 如果上述方法不起作用,请尝试此问题的技巧: 是否可以从具有超时的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) {
    ...
}

Lastly, the BluetoothSocket docs say that you can close the socket from any thread and that takes effect immediately. 最后, BluetoothSocket文档说您可以从任何线程关闭套接字并立即生效。 So you could simply have a watchdog thread and if the read call hasn't succeeded call close() on the socket which would cause the blocked read() to return with an error. 所以你可以简单地有一个看门狗线程,如果读取调用没有成功,则调用socket上的close() ,这会导致阻塞的read()返回错误。 This was what Dheeraj suggested above, but you only need to call close() when the other thread is stuck (due to network error/connection lost/etc): otherwise just check on its progress once in a while but don't close as long as your read hasn't taken too long. 这就是Dheeraj上面提到的,但是你只需要在另一个线程被卡住时调用close() (由于网络错误/连接丢失/等):否则只是偶尔检查一下它的进度但是不要关闭只要你的阅读时间不长。

It certainly looks like the lack of timeouts (and the impossibility of interrupting a blocked read() from the outside) has been a major ongoing pain point in Java for a long time. 这当然看起来缺乏超时(和()从外部中断阻塞读是不可能的),已经在Java中很长一段时间内正在进行的主要痛点。

See also: 也可以看看:

Is it possible to read from a InputStream with a timeout? 是否可以从具有超时的InputStream读取? (uses Callable / Future ) (使用Callable / Future

Can I set a timeout for a InputStream's read() function? 我可以为InputStream的read()函数设置超时吗? (uses Socket.setSoTimeout() ) (使用Socket.setSoTimeout()

How to kill a BufferedInputStream .read() call (uses InterruptibleChannel ) 如何杀死BufferedInputStream .read()调用 (使用InterruptibleChannel

How to stop a thread waiting in a blocking read operation in Java? 如何在Java中阻止线程在阻塞读取操作中等待?

Try this code which expands on my comment above: 试试这个代码,它扩展了我上面的评论:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM