【问题标题】:Find out whether an InputStream is closed查看 InputStream 是否关闭
【发布时间】:2019-01-20 14:58:16
【问题描述】:

在我尝试读取之前,Java 是否有任何可靠的方法来确定 java.io.InputStream 是否已关闭?

我的用例是我有一个方法,它接受 InputStream 参数并从中读取。该方法在自己的线程中运行,如果输入流关闭,我想终止线程。

InputStream 实现了Closeable,它提供了一个close() 方法,但显然无法查询实例是否已经关闭。

尝试从关闭的InputStream 读取会抛出IOException,但这可能有其他原因,并且接口合约中没有任何内容说明这种情况是否是永久性的,或者在某些情况下是否有机会它会在某个时候消失。

我的方法的调用者可以提供他们希望的InputStream 的任何子类,因此不能选择依赖特定的子类行为。

还有其他想法吗?

【问题讨论】:

  • 如果您使用Channel,则有isOpen()
  • InputStream 是一个抽象类。在您使用的子类中,您应该重写方法available(),如this API 文档中所述...
  • @zlakad 我有一个采用InputStream 参数的方法,调用者可以将他们希望的InputStream 的任何子类传递给该方法(只是编辑了我的问题以反映这一点)。
  • 好吧,@erickson 答案的最后一部分解释了你的问题。

标签: java inputstream


【解决方案1】:

没有。没有用于确定流是否已关闭的 API。

应用程序应该(并且通常)设计成没有必要明确地跟踪流的状态。流应该在 ARM 块中打开和可靠地关闭,并且在块内,假设流是打开的应该是安全的。当习惯性地使用 ARM 块时,它自然会限定对流的引用,以便在关闭后没有人可以访问它。

流可以通过多种方式在逻辑上“关闭”,并且许多流实现在调用read() 之前不会检测到这一点。例如,如果服务器关闭了一个套接字,则客户端中套接字对象的内部状态不太可能异步反映这一点;相反,下一次读取数据的调用将检测到闭包并更新状态。在这个例子中,如果套接字完全关闭,read() 调用将返回 EOF 以向应用程序发出信号,表明所有数据都已安全接收。如果连接异常终止,调用将抛出异常以指示某些数据可能已丢失。

可以合理地假设抛出IOException 的流已死,并且进一步尝试从中读取将继续失败。如果您调用read()之前可以检测到这种情况,您可能仍会以同样的方式处理它。

这种方法的例外是某些流支持读取超时,如果在一段时间内没有接收到输入,则会引发异常,但流仍然有效。只有调用者将这样的流传递给明确支持重试读取的方法才有意义。

【讨论】:

  • 很好,解释得很好。
  • 查看我修改后的问题。 InputStream 在传递时是打开的。但是,如果流是 Android 上的BluetoothInputStream,并且该函数生成一个线程,该线程循环从设备读取数据,并且设备超出范围,则流突然看起来关闭 - 这似乎是Android 的设计方式。
  • @user149408 这让我很担心:“……而且函数产生了一个线程……。”后台线程与 ARM 块交互时的一个常见问题是主线程立即返回并关闭后台仍然引用的流。确保这不是您所看到的。假设这对您来说不是问题,当设备超出范围时会抛出什么类型的IOException?在您的应用程序中,这种情况是否代表问题,例如可能不完整的命令?还是只是简单地,例如,要终止一个媒体流?
  • 我看到java.io.IOException: socket closed。在应用程序方面,这意味着我不再从设备接收任何数据。该设备是一个 RDS 接收器,它接收来自 FM 无线电频道的数据流。该应用程序会收听 TMC 交通报告,一些广播电台会播放这些报告。
  • @user149408 在这种情况下,实用的方法是处理来自read() 调用的任何IOException,就像处理来自isClosed() 调用的true 结果一样。如果InputStreamread() 上抛出异常,您可以假设它已失效并且永远不会返回更多数据;它是被进程中的另一个线程关闭,还是死于自然原因并不重要。对此的“异常”是超时异常,但这是需要与调用者协调的特殊情况。
【解决方案2】:

我认为最简洁的方法是添加一个自定义类,该类扩展您想要使用的任何类型的 InputStream。将其添加到您的 utils 包或其他内容中。并在该类中有一个名为:

private boolean isClosed = false;

因此,当您调用 close 时,您可以执行以下操作:

@Override
public void close() throws IOException {
    if(!isClosed){
        super.close();
        isClosed = true;
    }
}

public boolean isClosed() {
    return isClosed;
} 

在我看来,这将是满足您特定要求的最干净的方式。您还可以通过在不同情况下返回特定自定义错误的方式来管理错误。

【讨论】:

  • 我有一个采用InputStream 参数的方法,调用者可以将任何他们希望的 InputStream 子类传递给该方法(只是编辑了我的问题以反映这一点)——所以不幸的是,创建了所需的子类行为不是一种选择。
猜你喜欢
  • 2012-03-23
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
  • 2021-02-10
  • 1970-01-01
相关资源
最近更新 更多