4

I'm getting this annoying error in production that leads to crash my app.

I do PreferenceManager.getDefaultSharedPreferences(context).edit().putLong("key", value).apply(); to save many of my user based preferences

I'm using implementation 'androidx.preference:preference:1.0.0'

but still im getting many

Fatal Exception: java.util.ConcurrentModificationException
       at java.util.HashMap$HashIterator.nextEntry + 851(HashMap.java:851)
       at java.util.HashMap$KeyIterator.next + 885(HashMap.java:885)
       at com.android.internal.util.XmlUtils.writeSetXml + 355(XmlUtils.java:355)
       at com.android.internal.util.XmlUtils.writeValueXml + 693(XmlUtils.java:693)
       at com.android.internal.util.XmlUtils.writeMapXml + 300(XmlUtils.java:300)
       at com.android.internal.util.XmlUtils.writeMapXml + 269(XmlUtils.java:269)
       at com.android.internal.util.XmlUtils.writeMapXml + 235(XmlUtils.java:235)
       at com.android.internal.util.XmlUtils.writeMapXml + 192(XmlUtils.java:192)
       at android.app.SharedPreferencesImpl.writeToFile + 639(SharedPreferencesImpl.java:639)
       at android.app.SharedPreferencesImpl.-wrap2(SharedPreferencesImpl.java)
       at android.app.SharedPreferencesImpl$2.run + 535(SharedPreferencesImpl.java:535)
       at java.util.concurrent.ThreadPoolExecutor.runWorker + 1133(ThreadPoolExecutor.java:1133)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run + 607(ThreadPoolExecutor.java:607)
       at java.lang.Thread.run + 761(Thread.java:761)

I do know that many of my preference change occur in background threads, but the android api should be synchronized right?

I've no idea which changes are leading to the crash because as it runs in a separeted thread in android api i cant get more info about the crash

does anyone know what can be this? how to solve it without turn all apply into commit? how to get more info?

Rafael Lima
  • 3,079
  • 3
  • 41
  • 105
  • 1
    If you are saving a preference in a background thread, then you should use commit, there is no point in calling apply if you are already in a background thread, that's meant to avoid UI blocks, but if you're already off the UI, there's no point to call apply. – Francesc Jul 18 '19 at 16:41
  • "but the android api should be synchronized right?" -- ideally, yes, but perhaps it is not. `SharedPreferences` is not really designed for heavy use the way SQLite is. You may want to add your own synchronization layer across those writes. If you put your `SharedPreferences` I/O in a repository singleton, you have a natural place for the synchronization logic. – CommonsWare Jul 18 '19 at 16:43
  • @CommonsWare, creating a wrapper with singleton wouldn't solve because the exception happens after the `apply` call, it is an inner thread of `SharedPreferences` doing its job... – Rafael Lima Jul 19 '19 at 15:40
  • Sorry, I was also assuming that the repository would do its own thread management and use `commit()`. – CommonsWare Jul 19 '19 at 15:50

1 Answers1

1

I just ran into this issue as well, but it turns out not a thread safety issue in Shared Prefs itself, which is apparently thread safe (See https://stackoverflow.com/a/4695567/445348)

The problem turns out to be the Set passed into SharedPreferences.Editor.putStringSet(). If you pass in a Set and then modify that Set at the exact time that SharedPreferences iterates over to write it, it can throw this error.

To avoid this, make a copy of the Set before you pass it to putStringSet()

cottonBallPaws
  • 21,220
  • 37
  • 123
  • 171