Android LinkedList ConcurrentModificationException SurfaceView Thread

时间:2015-09-28 13:51:18

标签: android multithreading linked-list surfaceview concurrentmodification

我有一个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也会被迭代,以便在操作时将每个Canvas绘制到SurfaceView的{​​{1}}:

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

draw()的{​​{1}}方法由单独的SurfaceView调用:

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); } } } } LinkedList迭代之前暂停线程,并在完成循环后重新开始,以避免同时发生这两个修改。即使这似乎没有被推荐。我想知道如何在没有此错误的情况下迭代setActiveSticker(),或者是否有更好的方法来实现相同的功能。

2 个答案:

答案 0 :(得分:0)

这很正常。在循环时,您无法更改基础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;
        }

    }
}

答案 1 :(得分:0)

我找到了解决方案。我没有迭代LinkedListIteratorListIterator中的draw(),而是ConcurrentModificationException,我转换了LinkedList一个简单的数组,如:

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

我还保留了setActiveSticker()方法,就像我最初发布它一样,考虑到它没有产生任何错误。我在一篇名为&#34;避免ConcurrentModificationException的选项列表中找到了我正在寻找的答案:[A]多线程环境&#34; 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());
    }
}