ConcurrentModificationException с использованием оверлея mylocation и таймера - PullRequest
1 голос
/ 18 апреля 2011

эта проблема была моей головой, и я надеюсь, что вы можете помочь! Я уже нашел ответы на ряд вопросов здесь и надеюсь, что вы, люди, снова сможете творить чудеса:)

Хорошо, поэтому часть моего приложения использует API Карт Google для отображения наложения на карте (с использованием пользовательского класса наложения), а также определения местоположения пользователей с помощью mylocationoverlay. Теперь, если я не активирую оверлей mylocation, все работает нормально, но если он активирован, то приложение закрывается с ConcurrentModificationException.

Теперь таймер используется для обновления наложения, поскольку его положение постоянно меняется. Для этого я использовал асинхронную задачу, чтобы удалить наложение и добавить новую с обновленной позицией. Правильно ли предположить, что mylocation overlay выполняет аналогичные действия в фоновом режиме? Если это предположение верно, то я пришел к выводу, что mylocationoverlay и моя асинхронная задача пытаются выполнить итерацию / модификацию одного и того же массива одновременно. Единственное, я не знаю, как этого избежать!

Я не могу опубликовать свой код здесь, поскольку он очень большой, но я использовал пример кода, взятый из github, созданного commonsguy, и изменил его, чтобы вызвать ту же проблему.

Любая помощь будет принята с благодарностью, так как это действительно очень расстраивает меня. Заранее спасибо и простите за длинное описание!

Редактировать: Ссылка для общего кода - - 1012 *https://github.com/commonsguy/cw-advandroid/blob/master/Maps/NooYawkAsync/

Редактировать 2: добавлена ​​трассировка ошибок после кода Java.

package com.commonsware.android.maps;

import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.OverlayItem;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class NooYawk extends MapActivity {
    private MapView map=null;
    private MyLocationOverlay me=null;
    private SitesOverlay sites=null;
    public Timer timer;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        map=(MapView)findViewById(R.id.map);

        map.getController().setCenter(getPoint(40.76793169992044, -73.98180484771729));
        map.getController().setZoom(17);
        map.setBuiltInZoomControls(true);

        me=new MyLocationOverlay(this, map);
        me.enableMyLocation();
        map.getOverlays().add(me);

        timer = new Timer();
        timer.scheduleAtFixedRate(new RemindTask(), 10000, 10000);

        new OverlayTask().execute();
    }

    @Override
    public void onResume() {
        super.onResume();
        me.enableMyLocation();
        me.enableCompass();
    }       

    @Override
    public void onPause() {
        super.onPause();
        me.disableMyLocation();
        me.disableCompass();
    }       

    @Override
    protected boolean isRouteDisplayed() {
        return(false);
    }

    private GeoPoint getPoint(double lat, double lon) {
        return(new GeoPoint((int)(lat*1000000.0), (int)(lon*1000000.0)));
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////// TIMER CLASS //////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class RemindTask extends TimerTask {
    public void run() {
        new OverlayTask().execute();
    }
}


    private class SitesOverlay extends ItemizedOverlay<CustomItem> {
        private Drawable heart=null;
        private List<CustomItem> items=new ArrayList<CustomItem>();

        public SitesOverlay() {
            super(null);

            heart=getMarker(R.drawable.heart_full);

            items.add(new CustomItem(getPoint(40.748963847316034, -73.96807193756104),
                                                "UN", "United Nations", getMarker(R.drawable.blue_full_marker), heart));
            populate();
        }

        @Override
        protected CustomItem createItem(int i) {
            return(items.get(i));
        }

        @Override
        public void draw(Canvas canvas, MapView mapView, boolean shadow) {
            super.draw(canvas, mapView, shadow);

        }

        @Override
        public int size() {
            return(items.size());
        }

        void toggleHeart() {
            CustomItem focus=getFocus();

            if (focus!=null) {
                focus.toggleHeart();
            }

            map.invalidate();
        }

        private Drawable getMarker(int resource) {
            Drawable marker=getResources().getDrawable(resource);

            marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());
            boundCenter(marker);

            return(marker);
        }
    }

    class PopupPanel {
        View popup;
        boolean isVisible=false;

        PopupPanel(int layout) {
            ViewGroup parent=(ViewGroup)map.getParent();

            popup=getLayoutInflater().inflate(layout, parent, false);

            popup.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    hide();
                }
            });
        }

        View getView() {
            return(popup);
        }

        void show(boolean alignTop) {
            RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(
                        RelativeLayout.LayoutParams.WRAP_CONTENT,
                        RelativeLayout.LayoutParams.WRAP_CONTENT
            );

            if (alignTop) {
                lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                lp.setMargins(0, 20, 0, 0);
            }
            else {
                lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                lp.setMargins(0, 0, 0, 60);
            }

            hide();

            ((ViewGroup)map.getParent()).addView(popup, lp);
            isVisible=true;
        }

        void hide() {
            if (isVisible) {
                isVisible=false;
                ((ViewGroup)popup.getParent()).removeView(popup);
            }
        }
    }

    class CustomItem extends OverlayItem {
        Drawable marker=null;
        boolean isHeart=false;
        Drawable heart=null;

        CustomItem(GeoPoint pt, String name, String snippet, Drawable marker, Drawable heart) {
            super(pt, name, snippet);

            this.marker=marker;
            this.heart=heart;
        }

        @Override
        public Drawable getMarker(int stateBitset) {
            Drawable result=(isHeart ? heart : marker);

            setState(result, stateBitset);

            return(result);
        }

        void toggleHeart() {
            isHeart=!isHeart;
        }
    }

    class OverlayTask extends AsyncTask<Void, Void, Void> {
        @Override
        public void onPreExecute() {
            if (sites!=null) {
                map.getOverlays().remove(sites);
                map.postInvalidate();   
                sites=null;
            }
        }

        @Override
        public Void doInBackground(Void... unused) {
            //SystemClock.sleep(5000);                      // simulated work

            sites=new SitesOverlay();

            return(null);
        }

        @Override
        public void onPostExecute(Void unused) {
            map.getOverlays().add(sites);
            map.postInvalidate();           
        }
    }
}

Трассировка стека:

java.util.ConcurrentModificationException в java.util.ArrayList $ ArrayListIterator.next (ArrayList.java:573) на com.google.android.maps.OverlayBundle.draw (OverlayBundle.java:44) на com.google.android.maps.MapView.onDraw (MapView.java:494) на android.view.View.draw (View.java:6740) на android.view.ViewGroup.drawChild (ViewGroup.java:1640) на android.view.ViewGroup.dispatchDraw (ViewGroup.java:1367) на android.view.ViewGroup.drawChild (ViewGroup.java:1638) на android.view.ViewGroup.dispatchDraw (ViewGroup.java:1367) на android.view.ViewGroup.drawChild (ViewGroup.java:1638) на android.view.ViewGroup.dispatchDraw (ViewGroup.java:1367) на android.view.View.draw (View.java:6743) на android.widget.FrameLayout.draw (FrameLayout.java:352) на android.view.ViewGroup.drawChild (ViewGroup.java:1640) на android.view.ViewGroup.dispatchDraw (ViewGroup.java:1367) на android.view.View.draw (View.java:6743) на android.widget.FrameLayout.draw (FrameLayout.java:352) на com.android.internal.policy.impl.PhoneWindow $ DecorView.draw (PhoneWindow.java:1847) на android.view.ViewRoot.draw (ViewRoot.java:1407) на android.view.ViewRoot.performTraversals (ViewRoot.java:1163) на android.view.ViewRoot.handleMessage (ViewRoot.java:1727) на android.os.Handler.dispatchMessage (Handler.java:99) на android.os.Looper.loop (Looper.java:123) на android.app.ActivityThread.main (ActivityThread.java:4627) в java.lang.reflect.Method.invokeNative (родной метод) в java.lang.reflect.Method.invoke (Method.java:521) на com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:878) на com.android.internal.os.ZygoteInit.main (ZygoteInit.java:636) at dalvik.system.NativeStart.main (собственный метод)

1 Ответ

2 голосов
/ 24 апреля 2011

Почему вы удаляете и заменяете оверлей?

Почему бы не оставить оверлей в покое и поменять его маркер?Просто позвоните populate() снова на ItemizedOverlay, и он снова вызовет size() и getItem().Просто обязательно верните новые правильные данные.

Ваша ошибка определенно ощущается как связанная с добавлением и удалением оверлея.На самом деле, я подумал, что, возможно, ты делал это в doInBackground(), но это не так.Следовательно, я бы подумал, что то, что вы делаете, будет безопасным с точки зрения потоков, просто излишним с точки зрения обработки.

...