cordova-android: Crash Report: ConcurrentModificationException

Bug Report

Problem

We mentioned some Crash Reports from our App, build with cordova-android@8.1.0. Crashlog:

java.util.ConcurrentModificationException: java.util.ConcurrentModificationException

java.util.LinkedHashMap$LinkedHashIterator.nextNode LinkedHashMap.java:775
java.util.LinkedHashMap$LinkedValueIterator.next LinkedHashMap.java:803
org.apache.cordova.PluginManager.onResume PluginManager.java:262
org.apache.cordova.CordovaWebViewImpl.handleResume CordovaWebViewImpl.java:452
org.apache.cordova.CordovaActivity.onResume CordovaActivity.java:277
android.app.Instrumentation.callActivityOnResume Instrumentation.java:1465
android.app.Activity.performResume Activity.java:8203
android.app.ActivityThread.performResumeActivity ActivityThread.java:4757
android.app.ActivityThread.handleResumeActivity ActivityThread.java:4810
android.app.servertransaction.ResumeActivityItem.execute ResumeActivityItem.java:52
android.app.servertransaction.TransactionExecutor.executeLifecycleState TransactionExecutor.java:190
android.app.servertransaction.TransactionExecutor.execute TransactionExecutor.java:105
android.app.ActivityThread$H.handleMessage ActivityThread.java:2373
android.os.Handler.dispatchMessage Handler.java:107
android.os.Looper.loop Looper.java:213
android.app.ActivityThread.main ActivityThread.java:8147
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:513
com.android.internal.os.ZygoteInit.main ZygoteInit.java:1101

and

java.util.ConcurrentModificationException: java.util.ConcurrentModificationException

java.util.LinkedHashMap$LinkedHashIterator.nextNode LinkedHashMap.java:760
java.util.LinkedHashMap$LinkedValueIterator.next LinkedHashMap.java:788
org.apache.cordova.PluginManager.onPause PluginManager.java:209
org.apache.cordova.CordovaWebViewImpl.handlePause CordovaWebViewImpl.java:435
org.apache.cordova.CordovaActivity.onPause CordovaActivity.java:245
android.app.Activity.performPause Activity.java:7663
android.app.Instrumentation.callActivityOnPause Instrumentation.java:1536
android.app.ActivityThread.performPauseActivityIfNeeded ActivityThread.java:4726
android.app.ActivityThread.performPauseActivity ActivityThread.java:4691
android.app.ActivityThread.handlePauseActivity ActivityThread.java:4626
android.app.servertransaction.PauseActivityItem.execute PauseActivityItem.java:45
android.app.servertransaction.TransactionExecutor.executeLifecycleState TransactionExecutor.java:145
android.app.servertransaction.TransactionExecutor.execute TransactionExecutor.java:70
android.app.ActivityThread$H.handleMessage ActivityThread.java:2199
android.os.Handler.dispatchMessage Handler.java:112
android.os.Looper.loop Looper.java:216
android.app.ActivityThread.main ActivityThread.java:7625
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:524
com.android.internal.os.ZygoteInit.main ZygoteInit.java:987

Environment, Platform, Device

From Crashlog this happens to a Mate 20 Pro with Android 9 and a Galaxy Note10+ with Android 10 too.

Maybe related Issue?

Please fix this 😊

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 37 (37 by maintainers)

Commits related to this issue

Most upvoted comments

I think all iterations of the plugin map needs to be synchronized, so logically I think I believe if you have a large number of plugins, you’ll see a bigger impact. I’ll test by installing all the apache plugins + maybe a few more and try to compare apps on cordova-android@8.1 vs the fork.

This is all just speculation, so we’ll see just how big of the impact is. The impact could be insignificant altogether as well, for all we know…

No problem 😄 so as far as we know, the synchronizedMap wrapper is (hopefully) working. If you see a stack trace like that comes from 2.0.3+ then we still have a problem.

I didn’t use PR #1073, so can’t comment whether it works or not. On my first attempt to fix the issue, I only changed the collection to Collection.synchronizedMap, and did not change the for loop to foreach

I can confirm that just changing to using the synchronizedMap does not fix the issue.

I will add that even if PR #1073 works, it will not fix all ConcurrentModificationException, because CME occurs on other methods as well. It’s just that PostMessage has the highest occurrence because splashscreen plugin invokes it during app load, and that is when all other plugins are also loading at the same time.

My initial thought was using Collection.synchronizedMap was sufficient, but the exception still occurs in productions.

Are you saying the PR https://github.com/apache/cordova-android/pull/1073 did not address the issue for you?

Note the downside of doing this is that your app load may be abit slow as now the plugins different threads need to wait for the threads of another plugin.

Personally I don’t see a way around that if unrelated plugin code is causing conflicts. My motto is make the framework work, then find a way to optimise later if necessary. So I think that’s okay for now.

The 2 changesets that fix this problem. Feel free to use as you see fit.

A PR would be appreciated. I was never able to reproduce this issue myself, and the combination of plugins in my production apps doesn’t appear to trigger this issue so I’m relying on your observations. If you can prepare a branch with those commits and create a PR, it will make it easy for @EinfachHans to try it on his own apps to see if it resolves the issue for him.

Ok… well that means I think every loop block needs to be inside a synchronized block… but honestly I haven’t found any resources that says that is necessary, as every example deals with using the Iterator class… so this is just another assumption… The PluginManager has a lot of methods that does iterations over it’s two maps.

I’m not sure why for...in loops was chosen, I assumed for simplicity sake…

Ok but know really. I got the same Crash for the Version with the Fork… 😕 This Time really^^

Huawei P30 Lite (Android 9)

Yeah i can see that the User with the Crash is using Version 2.0.3 which is the one we build today

So just to let you know: We release a new Version with the Fork included tomorrow 🎉

Currently we had two Versions released before. Within these we had for now over 35 Crash Reports (nearly daily).

To be clear he was just simply responding to my question haha…

If it only happened 30 times since you opened the issue, that’s approximately 1 occurrence per day… which means the chances of intentionally triggering the bug naturally will likely be extremely difficult.

I have a feeling that there are potential race conditions in the resume/pause lifecycles but we will likely need a controlled test case to prove it…

Or if you’re willing… I can branch off of 8.1 and implement synchronizedMap and you can fork my branch. We’ll see if the problem goes away. If it is a race condition, the java docs appears to suggest that synchronizedMap should fix it by ensuring synchronised access to our plugin maps. I’m assuming this is at a performance cost though and I’m not sure how significant that would be…