Пытаюсь выяснить предупреждение «Ресурс не удалось закрыть» в моем приложении для Android

Я получал это предупреждение один раз в своем логарифме каждый раз, когда тестировал свое приложение.

W A resource failed to call close.

Я использовал следующий код, который нашел в Интернете, в своем методе onCreate(), чтобы вызвать исключение и трассировку стека, чтобы выяснить, что его вызвало.

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()).detectLeakedClosableObjects().build());

Это привело к следующей трассировке стека.

StrictMode policy violation: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
    at android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1994)
    at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:336)
    at java.io.FileInputStream.finalize(FileInputStream.java:508)
    at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
    at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
    at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
    at java.lang.Daemons$Daemon.run(Daemons.java:145)
    at java.lang.Thread.run(Thread.java:1012)
Caused by: java.lang.Throwable: Explicit termination method 'close' not called
    at dalvik.system.CloseGuard.openWithCallSite(CloseGuard.java:288)
    at dalvik.system.CloseGuard.open(CloseGuard.java:257)
    at java.io.FileInputStream.<init>(FileInputStream.java:176)
    at edu.cmu.pocketsphinx.Assets.getExternalItems(Assets.java:149)
    at edu.cmu.pocketsphinx.Assets.syncAssets(Assets.java:253)
    at com.example.atest.MainActivity$SetupTask.doInBackground(MainActivity.java:127)
    at com.example.atest.MainActivity$SetupTask.doInBackground(MainActivity.java:118)
    at android.os.AsyncTask$3.call(AsyncTask.java:394)
    at java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
    at java.lang.Thread.run(Thread.java:1012) 

который я проследил до этого кода..

private static class SetupTask extends AsyncTask<Void, Void, Exception> {
        WeakReference<MainActivity> activityReference;
        SetupTask(MainActivity activity) {
            this.activityReference = new WeakReference<>(activity);
        }
        @Override
        protected Exception doInBackground(Void... params) {
            try {
                Assets assets = new Assets(activityReference.get());
                File assetDir = assets.syncAssets();
                activityReference.get().setupRecognizer(assetDir);
            } catch (IOException e) {
                return e;
            }
            return null;
        }
        @Override
        protected void onPostExecute(Exception result) {
            if (result != null) {
                ((TextView) activityReference.get().findViewById(R.id.status_text)).setText(R.string.status_fail);
                Log.d(TAG, "SetupTask: Exception: " + result.getMessage());
                // TODO: decide what to do when there is an exception
            } else {
                Log.d(TAG, "SetupTask: startSearch()");
                activityReference.get().startSearch();
            }
        }
    }

Это код, который я получил от Pocketsphinx-android. Настройка ресурсов и распознавателя речи требует времени, поэтому они предложили сделать это в асинхронной задаче, чтобы предотвратить потерю кадров в основном потоке. Могу ли я сделать что-то очевидное, чтобы избавиться от этого предупреждения? Я протестировал синхронную версию этого кода, приведенную ниже, и получил ту же трассировку стека.

  try {
    Assets assets = new Assets(this);
    File assetDir = assets.syncAssets();
    setupRecognizer(assetDir);
    startSearch();
  } catch (IOException e) {
    Log.d(TAG, e.getMessage());
  }

  Я закрепил это дальше, чтобы ..

File assetDir = assets.syncAssets();

потому что я изменил это на..

File assetDir = assets.getExternalDir()

и я не получил никакого предупреждения. Похоже на проблему Pocketsphinx-Android.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
433
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ОП уже ответил на свой вопрос, мой ответ - показать, почему syncAssets() вызывает предупреждение logcat, а getExternalDir() нет.

Согласно исходному коду PocketSphinx , syncAssets() вызывает два внутренних метода: getItems() и getExternalItems(), оба из которых открываются InputStream (последний открывается FileInputStream). Например, в getItems():

Reader reader = new InputStreamReader(openAsset(path + HASH_EXT));

openAsset():

private InputStream openAsset(String asset) throws IOException {
    return assetManager.open(new File(SYNC_DIR, asset).getPath());
}

Вы получаете предупреждения из-за того, что встроенные InputStreams не закрываются.

Так это баг Pocketsphinx-android?

user1933677 07.05.2024 20:18

Да, это. Им следует переместить InputStream на локальный объект и закрыть его после использования.

Prajjwal Srivastav 08.05.2024 05:57

Другие вопросы по теме