Search code examples
javaandroidresourcesassetspocketsphinx-android

Trying to figure out "A resource failed to call close" warning in my Android app


I was getting this warning once in my logcat every time I tested my app..

W A resource failed to call close.

I used the following code I found online in my onCreate() method to throw an exception and stack trace to figure out what was causing it..

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

This produced the following stack trace..

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) 

which I've traced back to this code..

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();
            }
        }
    }

This is code I got from pocketsphinx-android. Setting up the Assets and the speech recognizer takes time so they suggested doing it in an Async task to prevent dropped frames on the main thread. Is there something obvious I can do to get rid of this warning? I tested the synchronous version of this code, below, and produces the same stack trace.

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

  I've pinned it down further to ..

File assetDir = assets.syncAssets();

'cause I changed that to..

File assetDir = assets.getExternalDir()

and I received no warning. Seems like a pocketsphinx-android problem.


Solution

  • OP already answered his own question, my answer is to show why syncAssets() causes a logcat warning whereas getExternalDir() does not.

    As per PocketSphinx source, the syncAssets() calls two internal methods, getItems() and getExternalItems() both of which open InputStream (latter opens FileInputStream). E.g in 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());
    }
    

    You get the warnings because of the inline InputStreams that are not being closed.