others-how to solve StrictMode policy violation: android.os.strictmode.LeakedClosableViolation?

1. Purpose

In this post, I will show you how to solve StrictMode policy violation: android.os.strictmode.LeakedClosableViolation, the detailed error stacktrace is as following:

2023-03-30 19:48:10.797 7553-7564/com.bswen.woreplycn D/StrictMode: 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. Callsite: close
        at android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1991)
        at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:338)
        at sun.nio.fs.UnixSecureDirectoryStream.finalize(UnixSecureDirectoryStream.java:580)
        at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:319)
        at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:306)
        at java.lang.Daemons$Daemon.run(Daemons.java:140)
        at java.lang.Thread.run(Thread.java:1012)

The code that enable StrictMode is as follows:

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
        .detectAll() 
        .penaltyLog()
        .penaltyDeath()
        .build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
        .detectLeakedSqlLiteObjects()
        .detectLeakedClosableObjects()
        .penaltyLog()
        .penaltyDeath()
        .build());

When I open the app , the app crashed and I don’t know what’s happening, how to debug it , where to find the code that cause the 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..



2. Solution

2.1 What is StrictMode in android?

StrictMode means strict mode and is a developer tool used to detect violations in programs. The most common scenario is to detect time-consuming operations such as local disk and network read and write in the main thread.

The StrictMode class is a tool class introduced by Android 2.3 (API 9), which can be used to help developers find some non-standard problems in the code, so as to improve the responsiveness of the application. For example, if the developer performs network operations or file system operations in the UI thread, these slow operations will seriously affect the responsiveness of the application, and even an ANR dialog box will appear.

Strict mode mainly detects two major problems, one is the thread policy, namely TreadPolicy, and the other is the VM policy, namely VmPolicy.

ThreadPolicy The content of thread policy detection is as following:

  • Custom time-consuming calls are enabled using detectCustomSlowCalls()
  • Disk read operations are enabled using detectDiskReads()
  • Disk write operations are enabled using detectDiskWrites()
  • Network operations are enabled using detectNetwork()

VmPolicy The content of virtual machine policy detection is as following:

  • Activity leaks are enabled using detectActivityLeaks()
  • Unclosed Closable object leaks are enabled using detectLeakedClosableObjects()
  • Leaked Sqlite objects are turned on with detectLeakedSqlLiteObjects()
  • The number of detected instances is enabled with setClassInstanceLimit()

2.2 How to enable/start StrictMode in android app?

The opening of strict mode can be placed in the onCreate method of Application or Activity and other components. In order to better analyze the problems in the application, it is recommended to put it in the onCreate method of Application.

if (IS_DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
  StrictMode.setVmPolicy(new VmPolicy.Builder().detectAll().penaltyLog().build());
}

Strict mode needs to be enabled in debug mode, not in release version.

At the same time, strict mode has been introduced since API 9, and some API methods have also been introduced from API 11. You should pay attention to the API level when using it. Partial strict mode can also be enabled if desired.

2.3 How to debug when app crash because of StrictMode in android?

There are many forms of reporting violations in strict mode, but if you want to analyze the specific violations, you still need to check the logs. Filter StrictMode in the terminal to get the specific stacktrace information of the violations.

You can just view the logcat or running logs of your app in android studio,or run the following command to get the log:

adb logcat | grep StrictMode

There is another method to debug the StrictMode: We can also enable strict mode in the developer options on your android phone. After enabling, if there is an operation that takes a long time in the main thread, the screen will flash. This is a more direct method.

2.4 The debug process of my app

As above shown , the error is as follows:

2023-03-30 19:48:10.797 7553-7564/com.bswen.woreplycn D/StrictMode: 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. Callsite: close
        at android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1991)
        at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:338)
        at sun.nio.fs.UnixSecureDirectoryStream.finalize(UnixSecureDirectoryStream.java:580)
        at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:319)
        at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:306)
        at java.lang.Daemons$Daemon.run(Daemons.java:140)
        at java.lang.Thread.run(Thread.java:1012)

No other information got from this error, so I need to check the onCreate/onResume method of my Main Activity.

I found this:

The onCreate of my app’s Main Activity:

        @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        isNeedNotificationListenerPerms(this); //contain much db operations
    }

Here is the code of isNeedNotificationListenerPerms method:

    public static boolean isNeedNotificationListenerPerms(Context context) {
        List<ReplyRule> rules = replyRuleDao.queryRules(context, true); //query database
        if(CollectionUtils.isNotEmpty(rules)) {
            for(ReplyRule rule:rules) {
                if(rule.getType()==Constants.RULE_TYPE_REJECT) {
                    return true;
                }
            }
        }

        return false;
    }

You can see that the above method isNeedNotificationListenerPerms contains heavy DB operations ,which will violate the VMPolicy of StrictMode.

So I found the problem.

2.5 The code that works for me

Here is the solution for me, I changed my main Activity.onCreate to call this method:

    Observable.create(new ObservableOnSubscribe<String>() {
                    @Override
                    public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                        boolean result = isNeedNotificationListenerPerms(MainActivity.this); //call it in IO thread pool
                        emitter.onNext(String.valueOf(result));
                    }
                }).subscribeOn(Schedulers.io())  //execute the db operations in seperate IO thread pool
                .observeOn(AndroidSchedulers.mainThread()) //when we got the result, execute on Main Thread
                .subscribe(new Observer<String>() {

                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(String result) {
                        //Do something on MainThread
                    }

                    @Override
                    public void onError(Throwable e) {
                        LogUtils.error("", e);
                    }

                    @Override
                    public void onComplete() {

                    }
                });

Here we use the RXJava to execute the db operation code in a seperate thread, after I got the result from DB, than I execute the other operations in the Main Thread.

    implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
    implementation "io.reactivex.rxjava2:rxjava:2.2.0"

2.6 Conclusion

To conclude, you should do the following to avoid violation of StrictMode:

  • If a file read/write violation occurs in the main thread, it is recommended to use a worker thread (combined with a Handler if necessary) to complete it.
  • If it is a write operation on SharedPreferences, it is recommended to call apply instead of commit on API 9 or above.
  • If there is an unclosed Closable object, close it according to the corresponding stacktrace.
  • If the SQLite object is leaked, release it according to the corresponding stacktrace.

If you call the following code in your main thread:

public void writeToExternalStorage() {
    File externalStorage = Environment.getExternalStorageDirectory();
    File destFile = new File(externalStorage, "dest.txt");
    try {
      OutputStream output = new FileOutputStream(destFile, true);
        output.write("droidyue.com".getBytes());
        output.flush();
        output.close();
    } catch (FileNotFoundException e) {
          e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
}

You will get this error:

D/StrictMode( 9730): StrictMode policy violation; ~duration=20 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=31 violation=2
D/StrictMode( 9730):    at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1176)
D/StrictMode( 9730):    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:106)
D/StrictMode( 9730):    at libcore.io.IoBridge.open(IoBridge.java:390)
D/StrictMode( 9730):    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
D/StrictMode( 9730):    at com.example.strictmodedemo.MainActivity.writeToExternalStorage(MainActivity.java:56)
D/StrictMode( 9730):    at com.example.strictmodedemo.MainActivity.onCreate(MainActivity.java:30)
D/StrictMode( 9730):    at android.app.Activity.performCreate(Activity.java:4543)

We can fix the above violation by running the disk operation code to a new thread as follows:

public void writeToExternalStorage() {
    new Thread() {
      @Override
        public void run() {
          super.run();
            File externalStorage = Environment.getExternalStorageDirectory();
            File destFile = new File(externalStorage, "dest.txt");
            OutputStream output = null;
            try {
                output = new FileOutputStream(destFile, true);
                output.write("droidyue.com".getBytes());
                output.flush();
                output.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != output) {
                    try {
                      output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }.start();
}



3. Summary

In this post, I demonstrated how to solve the StrictMode problem in my android app. That’s it, thanks for your reading.