8

I'm getting the following violation reported by StrictMode in Android.

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

It is cribbing about not closing streams properly. However, shouldn't closing in close the underlying streams? What could be the reason for the flagged error?

    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
        }
    }
Graham Borland
  • 60,055
  • 21
  • 138
  • 179
Roopesh Kohad
  • 97
  • 1
  • 4
  • Not sure how smart the StrictMode checker is, but it looks like its getting confused by your ***deferred close***, ie using a utility to close your stream for you. – Perception Mar 01 '12 at 12:08
  • In my case I get this error even when the `close()` is inline in the `finally` clause. – Graham Borland Sep 24 '12 at 13:26

4 Answers4

10

Looking at the source code, the constructors for both ObjectInputStream and BufferedInputStream can throw exceptions which would cause a FileInputStream object to be allocated on the following line, but the in variable will still be null:

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

Since in is null when we get to the finally block, that open FileInputStream object will not be closed by your closeQuietly() method, causing StrictMode to complain eventually :)

The simplest fix I would suggest is to split that allocation into 3 variables and call closeQuietly() on each, maybe something like this:

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;
}
Joe
  • 14,039
  • 2
  • 39
  • 49
  • Well spotted, but in my case there are no exceptions occurring. (I'll go back and double-check this to be absolutely sure.) – Graham Borland Sep 25 '12 at 12:23
  • Maybe I'm wrong here, but doesn't StrictMode complain on "possible scenarios" as well? It complains for example on IO access from the main thread, even though no ANR is actually caused. – dbm Sep 26 '12 at 12:55
  • 2
    It is supposed to complain about stuff which actually happens. In the case of IO access on the main thread, there really is IO access happening on the main thread. (It's not just theoretical.) So, it should only complain about the InputStream leak if it really has leaked. – Graham Borland Sep 27 '12 at 12:03
  • You get the bounty for finding a genuine problem in the OP's code which could cause a leak. I'm not the OP and my own code is very similar, but subtly different, and I've still to get completely to the bottom of it. – Graham Borland Sep 27 '12 at 20:20
  • Thanks! If you want help figuring out the problem in your code too, adding a link to it from here will most likely added several sets of new eyes to look at it as well :) – Joe Sep 27 '12 at 20:59
0

The code should work, unless you're using ProGuard which could mess up a little with bytecode.

FileInputStream has hooks to CloseGuard which is checked in finalize() if the instance was closed. That is why I think it should work. The question is weather close() was invoked or not?

I think that FileInputStream was created (because StrictMode thrown exception) but then a exception was thrown in finally and ignored somewhere.

    try {
        if (input != null) {
            input.close();
        }
    } catch (Exception ioe) {
        // check exception here
    }
pawelzieba
  • 16,082
  • 3
  • 46
  • 72
0
in = new ObjectInputStream(new BufferedInputStream(
                         new FileInputStream(mFile), STREAM_BUFFER_SIZE));

In this code sample you only close the ObjectInputStream but not the BufferedInputStream or the FileInputStream, you need to close them all.

Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
0

If you take a look at ObjectOutpuStream source, you will see that its close method closes underlying stream. Android's strict mode like many other code analysis tools have false positives which you can either ignore or rewrite your code so that it won't complain (inline closeQuietly method).

Konstantin Solomatov
  • 10,252
  • 8
  • 58
  • 88
  • Android's StrictMode is not a code analysis tool. It is implemented in source code look at `FileInputStream` and `CloseGuard` in `finalize()`. – pawelzieba Sep 24 '12 at 13:06