Android LinkedList ConcurrentModificationException SurfaceView 线程

Android LinkedList ConcurrentModificationException SurfaceView Thread

我有一个 SurfaceView,用户可以在其中绘制多个位图并进行修改(贴纸)。贴纸保存在 LinkedList 中,该 LinkedListMotionEvent.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 也被迭代以在操作时将每个绘制到 SurfaceViewCanvas

@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)

SurfaceViewdraw() 方法被单独的 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);
            }
        }
    }
}

我曾尝试在 LinkedListsetActiveSticker() 中迭代之前暂停线程,并在循环完成后恢复,以避免同时发生两个修改。尽管这似乎不被推荐。我想知道如何在不出现此错误的情况下迭代 LinkedList,或者是否有更好的方法来实现相同的功能。

这是正常的。循环时无法更改基础 Collection 。您可以使用具有添加和删除项目

ListIterator
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;
        }

    }
}

我找到了解决办法。我没有在 draw() 中迭代 LinkedListIteratorListIterator,它们都可以生成 ConcurrentModificationException,而是转换了 LinkedList到一个简单的数组,像这样:

Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for(Sticker sticker : stickers) {
    canvas.drawBitmap(sticker.getBitmap(), sticker.getMatrix(), sticker.getPaint());
}

考虑到它没有产生任何错误,我还保留了最初发布的 setActiveSticker() 方法。我在本文中标题为 "To Avoid ConcurrentModificationException In [A] Multi-Threaded Environment" 的一小部分选项中找到了我正在寻找的答案:How To Avoid ConcurrentModificationException When Using An Iterator.

编辑: 我的新绘制方法基于@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());
    }
}