StrictMode жалуется на то, что InputStream не закрывается

Я получаю сообщение о следующем нарушении с помощью StrictMode в Android.

02-05 04:07:41.190: ОШИБКА/StrictMode(15093): ресурс был получен в подключенной трассировке стека, но никогда не освобождался. См. java.io.Closeable для получения информации о предотвращении утечек ресурсов. 02-05 04:07:41.190: ОШИБКА/StrictMode(15093): java.lang.Throwable: метод явного завершения close не вызывается

Он пишет о неправильном закрытии потоков. Однако разве закрытие 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
        }
    }

person Roopesh Kohad    schedule 01.03.2012    source источник
comment
Не уверен, насколько умна программа проверки StrictMode, но похоже, что ее сбивает с толку ваше отложенное закрытие, то есть использование утилиты для закрытия вашего потока для вас.   -  person Perception    schedule 01.03.2012
comment
В моем случае я получаю эту ошибку, даже если close() встроен в предложение finally.   -  person Graham Borland    schedule 24.09.2012


Ответы (4)


Глядя на исходный код, конструкторы для обоих ObjectInputStream и BufferedInputStream может генерировать исключения, из-за которых объект FileInputStream будет размещен в следующей строке, но переменная in по-прежнему будет нулевым:

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

Поскольку in имеет значение null, когда мы добираемся до блока finally, этот открытый объект FileInputStream не будет закрыт вашим методом closeQuietly(), в результате чего 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;
}
person Joe    schedule 24.09.2012
comment
Хорошо замечено, но в моем случае исключений не бывает. (Я вернусь и перепроверю это, чтобы быть абсолютно уверенным.) - person Graham Borland; 25.09.2012
comment
Может быть, я ошибаюсь, но разве StrictMode не жалуется и на возможные сценарии? Например, он жалуется на доступ к вводу-выводу из основного потока, хотя на самом деле ANR не вызывается. - person dbm; 26.09.2012
comment
Предполагается, что нужно жаловаться на то, что происходит на самом деле. В случае доступа ввода-вывода в основном потоке действительно происходит доступ ввода-вывода в основном потоке. (Это не просто теория.) Таким образом, он должен жаловаться на утечку InputStream только в том случае, если она действительно произошла. - person Graham Borland; 27.09.2012
comment
Вы получаете вознаграждение за обнаружение настоящей проблемы в коде OP, которая может привести к утечке. Я не ОП, и мой собственный код очень похож, но немного отличается, и мне еще предстоит полностью разобраться в этом. - person Graham Borland; 28.09.2012
comment
Спасибо! Если вам также нужна помощь в выяснении проблемы в вашем коде, добавление ссылки на нее отсюда, скорее всего, добавит несколько наборов новых глаз, чтобы взглянуть на нее :) - person Joe; 28.09.2012

Если вы посмотрите на исходный код ObjectOutpuStream, вы увидите, что его метод close закрывает базовый поток. Строгий режим Android, как и многие другие инструменты анализа кода, имеет ложные срабатывания, которые вы можете либо игнорировать, либо переписать свой код, чтобы он не жаловался (встроенный метод closeQuietly).

person Konstantin Solomatov    schedule 01.03.2012
comment
Android StrictMode не является инструментом анализа кода. Это реализовано в исходном коде, посмотрите FileInputStream и CloseGuard в finalize(). - person pawelzieba; 24.09.2012

Код должен работать, если вы не используете ProGuard, который может немного испортить байт-код.

FileInputStream имеет хуки для CloseGuard, которые проверяются в finalize(), если экземпляр был закрыт. Вот почему я думаю, что это должно работать. Вопрос погода close() вызывалась или нет?

Я думаю, что FileInputStream было создано (потому что StrictMode выдало исключение), но затем в конце было выброшено исключение и где-то проигнорировано.

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

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

В этом примере кода вы закрываете только ObjectInputStream, но не BufferedInputStream или FileInputStream, вам нужно закрыть их все.

person Ilya Gazman    schedule 27.09.2012
comment
Ты? Это противоречит ответу Константина Соломатова. - person Graham Borland; 27.09.2012
comment
@GrahamBorland Просто запустите тест, и вы увидите. - person Ilya Gazman; 27.09.2012