我有一个SurfaceView
,用户可以绘制多个位图并对其进行修改(贴纸(。贴纸保存在一个LinkedList
中,该在MotionEvent.ACTION_DOWN
上迭代以查找用户正在触摸的贴纸:
private void setActiveSticker(float x, float y) {
Iterator<Sticker> stickersDesc = mStickers.descendingIterator();
while (stickersDesc.hasNext()) {
Sticker sticker = stickersDesc.next();
if (sticker.collider(x, y)) {
mActiveSticker = sticker;
mMode = MODE_DRAG;
break;
}
mStickers.remove(mActiveSticker);
mStickers.add(mActiveSticker);
}
}
此LinkedList
还被迭代,以便在操作SurfaceView
时将每个Canvas
绘制到它们:
@Override
public void draw (Canvas canvas) {
super.draw(canvas);
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
for (Sticker sticker : mStickers) {
sticker.draw(canvas);
}
}
这就是我得到ConcurrentModificationException
的地方:
09-28 08:56:41.769 19832-24370/com.example.ex E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-5279
Process: com.example.ex, PID: 19832
java.util.ConcurrentModificationException
at java.util.LinkedList$LinkIterator.next(LinkedList.java:124)
at com.example.ex.utilities.DrawingSurface.draw(DrawingSurface.java:133)
at com.example.ex.utilities.DrawingThread.onSurfaceUpdate(DrawingThread.java:95)
at com.example.ex.utilities.DrawingThread.run(DrawingThread.java:46)
SurfaceView
的draw()
方法由一个单独的Thread
调用:
public class DrawingThread extends Thread {
volatile boolean mRunning = false;
private long mRefreshRate;
private DrawingSurface mSurface;
public DrawingThread (DrawingSurface surface, long time) {
super();
mSurface = surface;
mRefreshRate = time;
}
public void setRunning (boolean run) {
mRunning = run;
}
@Override
public void run() {
while (mRunning) {
try {
sleep(mRefreshRate);
onSurfaceUpdate();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
public void onSurfaceChanged(Configuration config, Point fit, float ratio) {
float width, height;
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
width = fit.y * ratio;
height = fit.y;
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
width = fit.x;
height = fit.x / ratio;
} else {
width = fit.x;
height = fit.x / ratio;
} mSurface.getHolder().setFixedSize((int) width, (int) height);
}
private void onSurfaceUpdate() {
Canvas canvas = null;
try {
canvas = mSurface.getHolder().lockCanvas();
synchronized (mSurface.getHolder()) {
mSurface.draw(canvas);
}
} finally {
if (canvas != null) {
mSurface.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
我尝试在setActiveSticker()
迭代LinkedList
之前暂停线程,并在循环完成后恢复,以避免同时进行两种修改。即使这似乎不被推荐。我想知道如何在没有此错误的情况下迭代LinkedList
,或者是否有更好的方法来实现相同的功能。
正常的。循环时无法更改基础Collection
。您可以使用具有添加和删除项的列表迭代器
private void setActiveSticker(float x, float y) {
ListIterator<Sticker> stickersDesc = mStickers.descendingIterator();
while (stickersDesc.hasNext()) {
Sticker sticker = stickersDesc.next();
if (sticker.collider(x, y)) {
stickersDesc.remove();
stickersDesc.add(sticker);
mMode = MODE_DRAG;
return;
}
}
}
我找到了解决方案。我没有迭代LinkedList
、Iterator
或draw()
中的ListIterator
,它们都可以产生一个ConcurrentModificationException
,而是将LinkedList
转换为一个简单的数组,如下所示:
Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for(Sticker sticker : stickers) {
canvas.drawBitmap(sticker.getBitmap(), sticker.getMatrix(), sticker.getPaint());
}
我也保留了最初发布setActiveSticker()
方法,因为它没有产生任何错误。我在本文中标题为"避免 [A] 多线程环境中的 ConcurrentModificationException ">的一小部分选项中找到了我正在寻找的答案:使用迭代器时如何避免 ConcurrentModificationException。
编辑:我的新绘制方法基于@fadden的提示:
public void drawSurface(Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for (Sticker sticker : stickers) {
canvas.drawBitmap(sticker.getBitmap(), sticker.getMatrix(), sticker.getPaint());
}
}