Исключение ConcurrentModificationException с использованием наложения mylocation и таймера

#android #android-mapview #android-maps #concurrentmodification #mapactivity

#Android #android-mapview #android-карты #одновременное изменение #mapactivity

Вопрос:

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

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

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

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

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

Редактировать: Ссылка на код commonsguy — 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.Наложите bundle.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.просмотр.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.Метод.invokeNative (собственный метод) в java.lang.reflect.Метод.invoke (Method.java: 521) в com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636) в dalvik.system.NativeStart.main (собственный метод)

Комментарии:

1. Пожалуйста, предоставьте ссылку на конкретный код, на который вы ссылаетесь. github.com/commonsguy …?

2. Можете ли вы определить по выводимой ошибке, какая структура данных генерирует исключение?

3. Извините за это. ссылка есть — github.com/commonsguy/cw-advandroid/blob/master/Maps /… . Также добавлена ссылка на описание. Также я не могу точно сказать, что генерирует ошибку, я опубликую ошибку здесь через мгновение

Ответ №1:

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

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

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

Комментарии:

1. Спасибо за быстрый ответ, но я не совсем понимаю, что вы подразумеваете под изменением маркера. Вы имеете в виду метод getmarker()? Я не понимаю, как я могу установить новую ширину, lon таким образом. Извините, если это просто, но я все еще довольно новичок в этом.

2. @RadicalApps: Я мало что могу сказать, чего я не сказал в самом ответе: вызывайте populate() на ItemizedOverlay , и ваши методы size() и getItem() на вашем ItemizedOverlay возвращают правильные данные. Карта должна перерисовать себя. Не удаляйте наложение. Не добавляйте наложение. Просто вызовите populate() наложение, чтобы оно перезагрузило свои маркеры вашими свежими данными.

3. Ах да, теперь я слежу! Меня все еще смущал вопрос о том, должен ли я добавлять / удалять маркеры. Я попробую это через мгновение.

4. Ты настоящая легенда! Большое спасибо, что сработало как по волшебству: D Потребовалось немного времени, чтобы собраться с мыслями, но работает отлично! Скорее всего, я тоже подпишусь на ваши книги, они выглядят действительно интересными. Еще раз спасибо : D

5. @RadicalApps: Я мужчина. Возможно, я миф. Я сомневаюсь, что я легенда. Рад слышать, что у вас это сработало, и спасибо за добрые слова о книгах!