Skip to content

Android ConcurrentModificationException in AlphaTabRenderSurface.onLayout #2617

@AndreiGorelov

Description

@AndreiGorelov

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

The Android app with AlphaTabView sometimes crashes during screen rotation with:

java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1111)
at java.util.ArrayList$Itr.next(ArrayList.java:1064)
at alphaTab.platform.android.AlphaTabRenderSurface.onLayout(AlphaTabRenderSurface.kt:130)
at android.view.View.layout(View.java:25087)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1103)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.widget.ScrollView.onLayout(ScrollView.java:2620)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.HorizontalScrollView.onLayout(HorizontalScrollView.java:2120)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1103)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1961)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:1442)
at android.view.View.layout(View.java:25087)
at android.view.ViewGroup.layout(ViewGroup.java:6835)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:4962)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4275)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3151)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:11068)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1321)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1329)
at android.view.Choreographer.doCallbacks(Choreographer.java:930)
at android.view.Choreographer.doFrame(Choreographer.java:859)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1303)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8810)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Expected Behavior

Layout and rendering on Android should not crash due to concurrent modifications of the internal placeholder list. Updates to the collection used for layout should be safe even if new render partials arrive or existing ones are cleared during or around layout passes.

Steps To Reproduce

  1. Create a simple Android app screen that hosts alphaTab
  2. Enable user interaction and cursor in settings, similar to:
val settings = alphaTab.Settings().apply {
            player.playerMode = PlayerMode.EnabledAutomatic
            player.enableCursor = true
            player.enableAnimatedBeatCursor = true
            player.enableUserInteraction = true
            player.scrollMode = ScrollMode.OffScreen
            display.scale  = 0.8
        }
  1. Load a MusicXML score
  2. Interact with the score: scroll vertically/horizontally, rotate the device, let the view resize and re-layout.
  3. During or right after a layout, Android throws ConcurrentModificationException from AlphaTabRenderSurface.onLayout on the for (p in _placeholders) loop.

Link to jsFiddle, CodePen, Project

No response

Version and Environment

1. alphaTab version: 1.8.1
2. Platform: Android (Kotlin)
3. Devices: Android 14/16 tablet and phone

Platform

Android (Native)

Anything else?

Possible Fix:
In my fork I switched the collection used for _placeholders  to a thread-safe copy-on-write list to avoid ConcurrentModificationException and allow safe iteration while elements are being added or modified.
My fork commit for reference AndreiGorelov@e3f35a4

Metadata

Metadata

Assignees

Labels

area-coreRelated to some core parts of alphaTabplatform-androidRelated to the Android specific integrationsplatform-netRelated to the .net version of alphaTabstate-acceptedThis is a valid topic to work on.

Type

Projects

Status

No status

Relationships

None yet

Development

No branches or pull requests

Issue actions