【问题标题】:Input Stream Issue输入流问题
【发布时间】:2018-03-20 11:26:00
【问题描述】:

卡在一个问题上,

我正在使用BluetoothSocket 类,我在输入和输出流的帮助下发送和接收数据。

当应用程序从输入流中接收到大量数据时,我会强行终止我的应用程序,之后我会再次重新启动我的应用程序,但 InputStream 会返回我以前的数据,不再需要。如何丢弃旧的数据数据?

有没有人解决这个问题?

以下是我的源代码:

public class MyBluetoothService {
private static final String TAG = "MY_APP_DEBUG_TAG";
private Handler mHandler; // handler that gets info from Bluetooth service

// Defines several constants used when transmitting messages between the
// service and the UI.
private interface MessageConstants {
    public static final int MESSAGE_READ = 0;
    public static final int MESSAGE_WRITE = 1;
    public static final int MESSAGE_TOAST = 2;

    // ... (Add other message types here as needed.)
}

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;
    private byte[] mmBuffer; // mmBuffer store for the stream

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams; using temp objects because
        // member streams are final.
        try {
            tmpIn = socket.getInputStream();
        } catch (IOException e) {
            Log.e(TAG, "Error occurred when creating input stream", e);
        }
        try {
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "Error occurred when creating output stream", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        mmBuffer = new byte[1024];
        int numBytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs.
        while (true) {
            try {
                // Read from the InputStream.
                numBytes = mmInStream.read(mmBuffer);
                // Send the obtained bytes to the UI activity.
                Message readMsg = mHandler.obtainMessage(
                        MessageConstants.MESSAGE_READ, numBytes, -1,
                        mmBuffer);
                readMsg.sendToTarget();
            } catch (IOException e) {
                Log.d(TAG, "Input stream was disconnected", e);
                break;
            }
        }
    }

    // Call this from the main activity to send data to the remote device.
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);

            // Share the sent message with the UI activity.
            Message writtenMsg = mHandler.obtainMessage(
                    MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
            writtenMsg.sendToTarget();
        } catch (IOException e) {
            Log.e(TAG, "Error occurred when sending data", e);

            // Send a failure message back to the activity.
            Message writeErrorMsg =
                    mHandler.obtainMessage(MessageConstants.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString("toast",
                    "Couldn't send data to the other device");
            writeErrorMsg.setData(bundle);
            mHandler.sendMessage(writeErrorMsg);
        }
    }

    // Call this method from the main activity to shut down the connection.
    public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Could not close the connect socket", e);
            }
        }
    }
}

【问题讨论】:

  • 如果你正常关闭你的应用程序是否可以正常工作?(当 cancel() 被调用时)
  • 是的,它工作正常。 @KYHSGeekCode
  • 那么如何尝试实现终结器以确保关闭发生?
  • 在关闭套接字之前关闭流也更安全:)
  • 参考这个答案stackoverflow.com/a/158216/8614565

标签: java android inputstream android-bluetooth bluetooth-socket


【解决方案1】:
// Keep listening to the InputStream until an exception occurs

问题就在这里。您应该继续从流中读取,直到流结束或发生异常。如果read() 返回-1,您需要跳出读取循环。

目前您正在读取超出流的末尾,并且完全忽略了条件,因此最后一次成功读取时缓冲区中的数据当然仍然存在。

为了让您的应用程序保持看到该数据,您还必须忽略读取计数并假设缓冲区已填满,这也是无效的。

【讨论】:

    【解决方案2】:

    我认为你应该关闭套接字来管理错误。

    我建议您像下面的代码一样在终结器中执行此操作。

    private class ConnectedThread extends Thread
     {
     private final BluetoothSocket mmSocket;
     private final InputStream mmInStream;
     private final OutputStream mmOutStream;
     private byte[] mmBuffer; // mmBuffer store for the stream
     @override
     protected void finalize() throws Throwable
     {
       try
       {
           cancel();
       }
       finally
       {
            super.finalize();
       }
     }
    ...
    

    正如我在评论中提到的,在关闭套接字之前关闭每个streams 会更安全。

    所以,试试这个cancel() 方法。

    // Call this method from the main activity to shut down the connection.
     public void cancel()
     {
         try {
            mmInStream.close();
         } catch( NullPointerException | IOException e) {}
        try {
            mmOutStream.close();
         } catch( NullPointerException | IOException e) {}
    
         try
         {
             mmSocket.close();
         } catch (IOException e) {
             Log.e(TAG, "Could not close the connect socket", e);
         }
     }
    

    more information 关于finalize 方法。

    编辑:粗体建议比其他建议重要。

    阅读 EJP 的 cmets 我理解了为什么当您获得大数据时您的应用程序会停止:您可能需要在调用 read() 之前清除缓冲区。他说终结器可能不会被系统调用(我不知道为什么)。 如何 break read() 返回 -1时循环?

    刚才我发现了一个有用的link,关于读取流的正确方法。我希望它有所帮助。

    链接中引用的代码

    private String extract(InputStream inputStream) throws IOException
     {
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
                          byte[] buffer = new byte[1024];
          int read = 0;
          while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) {
              baos.write(buffer, 0, read);
          }   
              baos.flush();
            return new String(baos.toByteArray(), "UTF-8");
      }
    

    还有 虽然finalizer 可能无法在关闭sockets 之前被系统关闭streams 调用更安全(我之前读过一些SO 线程)。

    【讨论】:

    • 输入流中仍然有之前的数据。
    • 无法保证终结器方法将永远运行,也没有说明如何解决实际问题的原因。
    • 无法保证 garbage-collection 永远运行,更不用说终结者了。我只能建议您阅读 Javadoc。太宽泛了。
    • @KYHSGeekCode 无论何时。没有保证。
    • -1 测试和使用读取计数是最重要的。 read(buffer)read(buffer,0,buffer.length) 是等效的,因为它在 Javadoc 中明确说明。
    猜你喜欢
    • 2021-08-26
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2017-10-14
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多