PhilJay / MPAndroidChart

A powerful 🚀 Android chart view / graph view library, supporting line- bar- pie- radar- bubble- and candlestick charts as well as scaling, panning and animations.
Other
37.63k stars 9.02k forks source link

java.util.ConcurrentModificationException #5106

Open rangerzhou opened 3 years ago

rangerzhou commented 3 years ago

Summary

Expected Behavior

Possible Solution

Device (please complete the following information):

Additional Context

ADD A REWARD using Speed to SOLVE this issue QUICKLY and SUPPORT this project.

2021-03-05 16:02:57.387 14644-14644/com.aptiv.tempcode E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.aptiv.tempcode, PID: 14644
    java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.next(ArrayList.java:860)
        at com.github.mikephil.charting.renderer.ScatterChartRenderer.drawData(ScatterChartRenderer.java:41)
        at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:237)
        at android.view.View.draw(View.java:21564)
        at android.view.View.updateDisplayListIfDirty(View.java:20433)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4401)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4374)
        at android.view.View.updateDisplayListIfDirty(View.java:20393)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:575)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:581)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:654)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:3732)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3527)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2864)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1817)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7779)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1031)
        at android.view.Choreographer.doCallbacks(Choreographer.java:854)
        at android.view.Choreographer.doFrame(Choreographer.java:789)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1016)
        at android.os.Handler.handleCallback(Handler.java:914)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:227)
        at android.app.ActivityThread.main(ActivityThread.java:7582)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)

I do not known the reason, can anyone help me? Thanks!!!

KillerInk commented 3 years ago

thats not a problem from the chart itself. its more like how you pass the data to your list and get it. arraylist etc are not threadsafe producer adds new data while consumer read it. you have to syncronize your calls to the list to make sure the list gets not modified while a new item get added or it get read by the lib

rangerzhou commented 3 years ago

thats not a problem from the chart itself. its more like how you pass the data to your list and get it. arraylist etc are not threadsafe producer adds new data while consumer read it. you have to syncronize your calls to the list to make sure the list gets not modified while a new item get added or it get read by the lib

Thanks, but I had try to add synchronized in method, the exception still occured, then I execute update chart in runOnUiThread(), the exception gone.

KillerInk commented 3 years ago

syncronize methods only lock the execution for that method. what you need is something like

private ArrayList<YourItem> items = new ArrayList();

public void add(YourItem item)
{
        //lock the list. all other producers/consumers that want to get access to the list have to wait till the block is executed 
        syncronized(items)
        {
               items.add(item);
        }
}

public void updateChart(Chart chart)
{
        syncronized(items)
        {
                 chart.setList(items);
                 chart.invalidate();
        }
}

this works most time aslong you dont interact with the chart itself and force it internal to redraw. what i mean with that is like rotating the piechart. in that case it could happen that it throws a nullpointer if the index get accesed while the chart get updated. the chart internal access to the list need also to get synced. then it would be ready to handel realtime updates

why it works with runOnUiThread is because that stuff run one after one, also if get posted from a different handler wich runs on the MainLooper. Threading on android can get realy complicated.

rangerzhou commented 3 years ago

I know what you mean, thank you for your detailed answer!