Android 动画中的 java.util.ConcurrentModificationException
我怀念 Android 中同步代码的概念。
场景
屏幕上始终绘制 3 个项目。每个图像都存储在 ArrayList (lstGraphics) 中。为此,我使用 SurfaceView。一旦用户点击一张图像,该图像的市场就会被删除,并会添加一个新的市场。
代码示例:
AnimationHideThread
...
@Override
public void run() {
Canvas c;
while (run) {
c = null;
try {
c = panel.getHolder().lockCanvas(null);
synchronized (panel.getHolder()) {
panel.updatePhysics();
panel.manageAnimations();
panel.onDraw(c);
}
} finally {
if (c != null) {
panel.getHolder().unlockCanvasAndPost(c);
}
}
}
}
...
正如您首先看到的,我更新了Physics()。这意味着我计算每个图像移动的方向。在这里,我还将从列表中删除单击的图像。之后,我检查是否需要在manageAnimations() 的列表中添加一个新项目,然后最后一步绘制整个项目。
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
....
public void manageAnimations()
{
synchronized (this.getHolder()) {
...
while (lstGraphics.size()<3) {
lstGraphics.add(createRandomGraphic());
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (getHolder()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//... check if a image has been clicked and then set its property
graphic.setTouched(true);
}
}
return true;
}
}
public void updatePhysics() {
synchronized (getHolder()) {
for (Graphic graphic : lstGraphics) {
//.... Do some checks
if (graphic.isTouched())
{
lstGraphics.remove(graphic);
}
}
}
}
@Override
public void onDraw(Canvas canvas) {
/// draw the backgrounds and each element from lstGraphics
}
public class Graphic {
private Bitmap bitmap;
private boolean touched;
private Coordinates initialCoordinates;
....
}
我得到的错误是:
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): Uncaught handler: thread Thread-12 exiting due to uncaught exception
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): java.util.ConcurrentModificationException
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:66)
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at com.test.customcontrols.Panel.updatePhysics(Panel.java:290)
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at com.test.customcontrols.AnimationHideThread.run(AnimationHideThread.java:41)
非常感谢任何帮助。谢谢。
There is something I miss with the notion of Synchronizing code in Android.
Scenario
There are always 3 items drawn on the screen. Each image is stored in a ArrayList (lstGraphics). For this purpose I use a SurfaceView. Once the user taps on a image, the image get's market to be removed and a new one will be added.
Code samples:
AnimationHideThread
...
@Override
public void run() {
Canvas c;
while (run) {
c = null;
try {
c = panel.getHolder().lockCanvas(null);
synchronized (panel.getHolder()) {
panel.updatePhysics();
panel.manageAnimations();
panel.onDraw(c);
}
} finally {
if (c != null) {
panel.getHolder().unlockCanvasAndPost(c);
}
}
}
}
...
So as you can seem first I updatePhysics(). This means I calculate direction where each image will move to. In here I will also remove clicked images from my list. After that I check if I need to add a new Item in my list in manageAnimations() and then the final step draw the whole thing.
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
....
public void manageAnimations()
{
synchronized (this.getHolder()) {
...
while (lstGraphics.size()<3) {
lstGraphics.add(createRandomGraphic());
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (getHolder()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//... check if a image has been clicked and then set its property
graphic.setTouched(true);
}
}
return true;
}
}
public void updatePhysics() {
synchronized (getHolder()) {
for (Graphic graphic : lstGraphics) {
//.... Do some checks
if (graphic.isTouched())
{
lstGraphics.remove(graphic);
}
}
}
}
@Override
public void onDraw(Canvas canvas) {
/// draw the backgrounds and each element from lstGraphics
}
public class Graphic {
private Bitmap bitmap;
private boolean touched;
private Coordinates initialCoordinates;
....
}
The error I get is:
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): Uncaught handler: thread Thread-12 exiting due to uncaught exception
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): java.util.ConcurrentModificationException
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:66)
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at com.test.customcontrols.Panel.updatePhysics(Panel.java:290)
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at com.test.customcontrols.AnimationHideThread.run(AnimationHideThread.java:41)
Any help is greatly appreciated. Thank you.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的问题出在您的物理方法中,您在其中添加图形并列出
for(Graphicgraphic : lstGraphics)
和lst.Graphics.remove(graphic);
的组合导致 ConcurrentModificationException,因为您正在运行列表并同时尝试修改它。到目前为止,我知道两种解决方案:
如果有可用的迭代器,请使用迭代器(到目前为止从未针对 Android 进行过编码)。
使用第二个列表来存储要删除的元素,然后再删除它们
Your problem is in your physics method, where you add the graphic and the list
the combination of
for(Graphic graphic : lstGraphics)
andlst.Graphics.remove(graphic);
causes the ConcurrentModificationException because you are running over your list and concurrently try to modify it.So far I know two solutions:
Use an Iterator instead if one is available (never coded for Android so far).
use a second list to store the elements to remove and remove them afterwards
正如 @idefix 所说,您可以在单线程上下文中轻松获得 ConcurrentModificationException,如下所示:
As @idefix said, you can easily get ConcurrentModificationException in single-threaded context like this:
您可以使用 CopyOnWriteArrayList 如下所示:
You can use CopyOnWriteArrayList like below:
这是我使用@idefix第二个解决方案的方法:
谢谢@idefix +1
This is my method using @idefix second solution:
Thanks @idefix +1