StrictMode抱怨InputStream没有被关闭。

8
我在Android中使用StrictMode时,出现了以下违规报告:

02-05 04:07:41.190: ERROR/StrictMode(15093): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks. 02-05 04:07:41.190: ERROR/StrictMode(15093): java.lang.Throwable: Explicit termination method 'close' not called

它抱怨没有正确关闭流。然而,关闭in不应该关闭底层流吗?为什么会出现标记错误的原因是什么?
    private ArrayList<Uri> loadPath() {
        ArrayList<Uri> uris = new ArrayList<Uri>();
        if (mFile.exists()) {
            ObjectInputStream in = null;
            try {
                in = new ObjectInputStream(new BufferedInputStream(
                         new FileInputStream(mFile), STREAM_BUFFER_SIZE));
                ArrayList<String> strings = new ArrayList<String>();
                strings.addAll((ArrayList<String>) in.readObject());
                for (String string : strings) {
                    uris.add(Uri.parse(string));
                }
            } catch (Exception e) {
                mFile.delete();
            } finally {
                IOUtils.closeQuietly(in);
            }
        }
        return uris;
     }

    public static void closeQuietly(InputStream input) {
        try {
            if (input != null) {
                input.close();
            }
        } catch (IOException ioe) {
            // ignore
        }
    }

不确定StrictMode检查器有多聪明,但看起来它被你的“deferred close”弄糊涂了,即使用工具来替你关闭流。 - Perception
在我的情况下,即使close()finally子句中是内联的,我仍然会收到此错误。 - Graham Borland
4个回答

10

查看源代码,ObjectInputStreamBufferedInputStream的构造函数可能会抛出异常,导致以下一行分配了一个FileInputStream对象,但是变量in仍将为null:

            in = new ObjectInputStream(
                    new BufferedInputStream(
                            new FileInputStream(mFile), 
                    STREAM_BUFFER_SIZE)
            );

由于当我们到达finally块时,in为null,因此你的closeQuietly()方法将不会关闭这个打开的FileInputStream对象,最终导致StrictMode抱怨 :)

我建议最简单的修复方式是将该分配拆分为3个变量,并对每个变量调用closeQuietly(),就像这样:

private ArrayList<Uri> loadPath() {
    final ArrayList<Uri> uris = new ArrayList<Uri>();
    if (mFile.exists()) {
        ObjectInputStream ois = null;
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            fis = new FileInputStream(mFile);
            bis = new BufferedInputStream(fis, STREAM_BUFFER_SIZE);
            ois = new ObjectInputStream(bis);
            final ArrayList<String> strings = new ArrayList<String>();
            strings.addAll((ArrayList<String>) ois.readObject());
            for (final String string : strings) {
                uris.add(Uri.parse(string));
            }
        } catch (final Exception e) {
            mFile.delete();
        } finally {
            closeQuietly(fis);
            closeQuietly(bis);
            closeQuietly(ois);
        }
    }
    return uris;
}

很敏锐,但在我的情况下没有发生任何异常。(我会回去再次检查以确保。) - Graham Borland
也许我说错了,但是 StrictMode 不也会对 "可能的情况" 进行投诉吗?例如,它会在主线程上进行 IO 访问时进行投诉,即使实际上没有导致 ANR。 - dbm
2
它应该抱怨实际发生的事情。在主线程上进行IO访问的情况下,确实会发生IO访问。(这不仅仅是理论上的。)因此,只有在它真正泄漏时才应该抱怨InputStream泄漏。 - Graham Borland
你会因为在 OP 的代码中发现一个真正可能导致泄漏的问题而获得赏金。我不是 OP,我的代码非常相似,但微妙地不同,我仍然没有完全搞清楚它的底层原理。 - Graham Borland
谢谢!如果你需要帮助解决代码问题,将其链接在这里,很可能会吸引到更多人来查看它 :) - Joe

0

代码应该可以正常工作,除非您正在使用ProGuard,这可能会对字节码造成一些混乱。

FileInputStream具有钩子到CloseGuard,在finalize()中检查实例是否已关闭。这就是为什么我认为它应该可以工作的原因。问题是是否调用了close()

我认为FileInputStream已经被创建(因为StrictMode抛出异常),但最后抛出了一个异常并被忽略了。

    try {
        if (input != null) {
            input.close();
        }
    } catch (Exception ioe) {
        // check exception here
    }

0
in = new ObjectInputStream(new BufferedInputStream(
                         new FileInputStream(mFile), STREAM_BUFFER_SIZE));

在这个代码示例中,你只关闭了ObjectInputStream,但没有关闭BufferedInputStreamFileInputStream,你需要全部关闭它们。

你这么说与Konstantin Solomatov的回答相矛盾。 - Graham Borland
@GrahamBorland 只需对其进行测试,您就会看到。 - Ilya Gazman

0
如果您查看ObjectOutpuStream源代码,您会发现它的close方法关闭了底层流。Android的严格模式以及许多其他代码分析工具都存在误报问题,您可以忽略它们或重写您的代码,使其不再出现警告(使用inline closeQuietly方法)。

Android的StrictMode不是一个代码分析工具。它是在源代码中实现的,可以查看FileInputStreamCloseGuard中的finalize()函数。 - pawelzieba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接