简体   繁体   中英

Still getting concurrent modification error

Ok, so I've been getting the concurrent modification exception while trying to edit specific objects in my array. From my understanding this occurs when you try to edit an array while iterating through it at the same time. I tried using an iterator and synchronizing everything that accessed the array, but with no luck. After a little research I found that some people just pass the values to another array in order to edit the first array (I know it's confusing). So I did just that my code now looks something like

public static void checkECount(int id) {
        canUpdate = false;
        enemiesList.remove(id);
        enemyCount = enemyCount - 1;

        ArrayList<Integer> arCount = new ArrayList<Integer>();
        int index = 0;

        for(Sprite s : enemiesList) {
              arCount.add(index);
              index++; 
            }
         for(Integer i : arCount){
             Sprite ss = enemiesList.get(i);
             ss.setID(i);
            }

        arCount.clear();
        canUpdate = true;

    }

Problem is I'm still getting the same freaking error! However once I comment out the enemiesList.remove(id); line it works fine (well it doesn't do what I want, but it doesn't throw the error anymore). Does anyone know whats going on here? I'm going to add the rest of the code and the logcat at the bottom in case anyone needs them. Please help if you can, this error is turning into the bane of my freakin existence.

package com.gametest;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;

public class GameSurfaceView extends Activity implements OnTouchListener {

    double ran;
    int touchX, touchY, screenWidth, screenHeight, objX, objY;
    static boolean canUpdate;
    static int enemyCount;
    static MyView v;
    static Bitmap orb, explosion;
    static List<Sprite> enemiesList = new ArrayList<Sprite>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        v = new MyView(this);
        v.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent me) {
                touchX = (int) me.getX();
                touchY = (int) me.getY();
                for (Sprite sprite : enemiesList) {
                    sprite.checkTouch(touchX, touchY);
                }

                return true;
            }
        });
        canUpdate = true;
        ran = 0;
        orb = BitmapFactory.decodeResource(getResources(), R.drawable.blue_orb);
        explosion = BitmapFactory.decodeResource(getResources(), R.drawable.explosion);
        createEnemies();
        setContentView(v);
    }



    private void createEnemies() {
        if (enemyCount < 5) {
            screenWidth = v.getWidth();
            screenHeight = v.getHeight();
            int listLength = enemiesList.size();
            enemiesList.add(new Sprite(v, orb, explosion, screenWidth, screenHeight, listLength));
            enemyCount = enemyCount + 1;
        }
    }

    public static void checkECount(int id) {
        canUpdate = false;
        enemiesList.remove(id);
        enemyCount = enemyCount - 1;

        ArrayList<Integer> arCount = new ArrayList<Integer>();
        int index = 0;

        for(Sprite s : enemiesList) {
              arCount.add(index);
              index++; 
            }
         for(Integer i : arCount){
             Sprite ss = enemiesList.get(i);
             ss.setID(i);
            }

        arCount.clear();
        canUpdate = true;

    }

    @Override
    protected void onPause() {
        super.onPause();
        v.pause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        v.resume();
    }

    public class MyView extends SurfaceView implements Runnable {

        Thread t = null;
        SurfaceHolder holder;
        boolean isItOk = false;

        public MyView(Context context) {
            super(context);
            holder = getHolder();

        }

        @Override
        public void run() {
            while (isItOk == true) {
                if (!holder.getSurface().isValid()) {
                    continue;
                }
                Canvas c = holder.lockCanvas();
                    if(canUpdate){
                        canvas_draw(c);
                    }
                holder.unlockCanvasAndPost(c);

            }

        }

        protected void canvas_draw(Canvas canvas) {
            canvas.drawARGB(255, 50, 10, 10);
            String ranString = Integer.toString(screenHeight);
            ran = Math.random() * 5;
            if (ran > 3) {
                createEnemies();
            }

            Paint paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setTextSize(15);
            canvas.drawText(ranString, 10, screenHeight - 25, paint);
            for (Sprite sprite : enemiesList) {
                sprite.sprite_draw(canvas);
            }
        }

        public void pause() {
            isItOk = false;
            while (true) {
                try {
                    t.join();
                } catch (InterruptedException e) {

                }
                break;
            }
            t = null;
        }

        public void resume() {
            isItOk = true;
            t = new Thread(this);
            t.start();
        }

    }

    @Override
    public boolean onTouch(View arg0, MotionEvent arg1) {
        // TODO Auto-generated method stub
        return false;
    }

}

And heres the logcat

01-29 21:58:51.140: E/AndroidRuntime(1135): FATAL EXCEPTION: main
01-29 21:58:51.140: E/AndroidRuntime(1135): java.util.ConcurrentModificationException
01-29 21:58:51.140: E/AndroidRuntime(1135):     at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at com.gametest.GameSurfaceView$1.onTouch(GameSurfaceView.java:41)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.View.dispatchTouchEvent(View.java:7122)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1877)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1877)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1877)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1925)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1379)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.app.Activity.dispatchTouchEvent(Activity.java:2396)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1873)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.View.dispatchPointerEvent(View.java:7307)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3172)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3117)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4153)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4132)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4224)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:171)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.os.MessageQueue.nativePollOnce(Native Method)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.os.MessageQueue.next(MessageQueue.java:125)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.os.Looper.loop(Looper.java:124)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at android.app.ActivityThread.main(ActivityThread.java:4745)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at java.lang.reflect.Method.invokeNative(Native Method)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at java.lang.reflect.Method.invoke(Method.java:511)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
01-29 21:58:51.140: E/AndroidRuntime(1135):     at dalvik.system.NativeStart.main(Native Method)

Interesting, I seem to remember seeing this code a few days ago. :)

I believe you are allowing other threads to iterate over the list while your main thread is removing elements from it willy nilly.

Take a look at this:

v.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent me) {
        touchX = (int) me.getX();
        touchY = (int) me.getY();
        for (Sprite sprite : enemiesList) {
            sprite.checkTouch(touchX, touchY);
        }
        return true;
    }
});

This allowes another thread to iterate over the list.

MyView also runs on another thread, ( implements Runnable ) and it also foreach es over the list.

What you want, is some kind of thread-safe list. Take a look at the CopyOnWriteArrayList , I think it'll solve your problems. Use the iterator provided by the list to iterate over it.

Try using synchronized while removing from list

synchronized(this){
    enemiesList.remove(id);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM