【问题标题】:Why am I getting a ConcurrentModificationException?为什么我会收到 ConcurrentModificationException?
【发布时间】:2020-11-08 17:21:03
【问题描述】:

我有一个锻炼追踪器应用。

用户可以记录锻炼(例如重量、次数)。

它工作正常,但是当您在几个月之间更改然后修改记录的锻炼(例如删除/更新)或记录新的锻炼时,应用程序会崩溃。

我收到了concurrentModificationException

我已经阅读了一些关于错误消息的文章,但是我仍然不知道为什么会收到错误消息。

为什么我会收到concurrentModificationException

错误信息:

java.util.ConcurrentModificationException
        at java.util.ArrayList$SubList.size(ArrayList.java:1057)
        at java.util.AbstractCollection.toArray(AbstractCollection.java:136)
        at java.util.ArrayList.addAll(ArrayList.java:588)
        at com.example.exerciseappv5.CalendarCode.CustomCalendarView.checkIfWeekCompleted(CustomCalView.java:181)
        at com.example.exerciseappv5.CalendarCode.CustomCalendarView.lambda$ChangeMonth$0$CustomCalView(CustomCalView.java:167)

发生错误的行:

if (completedDates.equals("100")) {completedDatesList.addAll(thisWeekDates);}

CustomCalendarView(相关代码)


 public class CustomCalendarView extends LinearLayout {

 
    ArrayList<Date> dates = new ArrayList<>();
    ArrayList<String> datesFormattedList = new ArrayList<>();
    ArrayList<String> logDatesList = new ArrayList<>();

    CopyOnWriteArrayList<String> completedDatesList = new CopyOnWriteArrayList<>
();
    ArrayList<String> uniqueDatesWithLogEntries = new ArrayList<>();
    ArrayList<List<String>> listOfWeeks = new ArrayList<>();

    public CustomCalendarView(Context context) {
        super(context);
    }

    public CustomCalendarView(final Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        InitializeLayout();
        SetUpCalendar();

        PreviousButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                calendar.add(Calendar.MONTH, -1);
                SetUpCalendar();
            }
        });

        NextButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                calendar.add(Calendar.MONTH, 1);
                SetUpCalendar();
            }
        });

        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                builder.setCancelable(true);
                final String date = eventDateFormat.format(dates.get(position));
                final ArrayList<String> arrayListDateFormattedList = (ArrayList<String>) datesFormattedList;
                Intent i = new Intent(getContext(), WorkoutButtonsActivity.class);
                i.putExtra(WorkoutButtonsActivity.EXTRA_DATE, date);
                i.putStringArrayListExtra(WorkoutButtonsActivity.EXTRA_VISIBLE_DATES, arrayListDateFormattedList);
                getContext().startActivity(i);
            }
        });
    }

    public CustomCalendarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void InitializeLayout() {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.calendar_layout, this);
        NextButton = view.findViewById(R.id.nextBtn);
        PreviousButton = view.findViewById(R.id.previousBtn);
        CurrentDate = view.findViewById(R.id.current_Date);
        gridView = view.findViewById(R.id.gridview);
    }

    void SetUpCalendar() {

        datesFormattedList.clear();
        String currentDate = dateFormat.format(calendar.getTime());  // e.g. July 2020
        CurrentDate.setText(currentDate);
        dates.clear();
        Calendar monthCalendar = (Calendar) calendar.clone();
        monthCalendar.set(Calendar.DAY_OF_MONTH, 1);
        int FirstDayofMonth = monthCalendar.get(Calendar.DAY_OF_WEEK) - 2;
        monthCalendar.add(Calendar.DAY_OF_MONTH, -FirstDayofMonth);

        while (dates.size() < MAX_CALENDAR_DAYS) {
            dates.add(monthCalendar.getTime());
            monthCalendar.add(Calendar.DAY_OF_MONTH, 1);
        }
        /*CONVERTS THE LIST OF ALL VISIBLE DATES TO A STRING */
        for (int i = 0; i < MAX_CALENDAR_DAYS; i++) {
            final String dateFormatted = eventDateFormat.format(dates.get(i));
            datesFormattedList.add(dateFormatted);
        }
        ChangeMonth();
    }

    public void ChangeMonth() {
        completedDatesList.clear();
        listOfWeeks.clear();
        //Break Down month dates into weeks
        listOfWeeks.add(datesFormattedList.subList(0, 7));          //1
        listOfWeeks.add(datesFormattedList.subList(7, 14));         //2
        listOfWeeks.add(datesFormattedList.subList(14, 21));        //3
        listOfWeeks.add(datesFormattedList.subList(21, 28));        //4
        listOfWeeks.add(datesFormattedList.subList(28, 35));        //5
        listOfWeeks.add(datesFormattedList.subList(35, 42));        //6

        // For loop query to check the percentage completion fo each week
        for (int i = 0; i < listOfWeeks.size(); i++) {
            List<String> thisWeekDates =  listOfWeeks.get(i);
            workoutCompletionPercentageViewModel = ViewModelProviders.of((FragmentActivity) context).get(WorkoutCompletionPercentageViewModel.class);
            workoutCompletionPercentageViewModel.getWeekCompletionPercentage(thisWeekDates).observe((FragmentActivity) context,
                    completedDates -> checkIfWeekCompleted(completedDates, thisWeekDates)
            );
        }

        //Observe data from log entry view model to determine whether calendar date has a log entry
        logEntriesViewModel = ViewModelProviders.of((FragmentActivity) context).get(LogEntriesViewModel.class);
        logEntriesViewModel.setFilter(datesFormattedList);
        logEntriesViewModel.getDatesFilteredByMonth().observe((FragmentActivity) context, logDates -> setData(logDates));
    }

    public void checkIfWeekCompleted(String weekCompletion, List<String> thisWeekDates) {

        if (weekCompletion.equals("100")) {
            completedDatesList.addAll(thisWeekDates);
        }
    }

    private void setData(List<String> logDates) {

        logDatesList.clear();
        logDatesList.addAll(logDates);
        Set<String> setWithUniqueValues = new HashSet<>(logDatesList);
        uniqueDatesWithLogEntries = new ArrayList<>(setWithUniqueValues);
        myGridAdapter = new MyGridAdapter(context, dates, calendar, uniqueDatesWithLogEntries, completedDatesList);
        gridView.setAdapter(myGridAdapter);
    }
}

MyGridAdapter(我不确定这是否相关)

public class MyGridAdapter extends ArrayAdapter {
    List<Date> dates;
    CopyOnWriteArrayList<String> completedDates;
    Calendar currentDate;
    List<String> logs;
    LayoutInflater inflater;
    ImageView fire;
    LayerDrawable bottomBorder;

    private WorkoutCompletionPercentageViewModel workoutCompletionPercentageViewModel;

    public MyGridAdapter(@NonNull Context context, List<Date> dates, Calendar currentDate, List<String> logs, CopyOnWriteArrayList<String> completedDates) {
        super(context, R.layout.single_cell_layout);

        this.completedDates = completedDates;
        this.dates = dates;
        this.currentDate = currentDate;
        this.logs = logs;

        inflater = LayoutInflater.from(context);
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Date monthDate = dates.get(position);
        Calendar dateCalendar = Calendar.getInstance();
        dateCalendar.setTime(monthDate);
        int DayNo = dateCalendar.get(Calendar.DAY_OF_MONTH);
        int displayMonth = dateCalendar.get(Calendar.MONTH) + 1;
        int displayYear = dateCalendar.get(Calendar.YEAR);
        int currentMonth = currentDate.get(Calendar.MONTH) + 1;
        int currentYear = currentDate.get(Calendar.YEAR);
        int currentDay = currentDate.get(Calendar.DAY_OF_MONTH);


        View view = convertView;
        if (view == null) {
            view = inflater.inflate(R.layout.single_cell_layout, parent, false);
        }

        fire = view.findViewById(R.id.fire);
        fire.setVisibility(View.INVISIBLE);

        if (displayMonth == currentMonth && displayYear == currentYear) {
            view.setBackgroundColor(getContext().getResources().getColor(R.color.green));

        } else {
            view.setBackgroundColor(Color.parseColor("#cccccc"));
        }

        ImageView calendarTick = view.findViewById(R.id.calendarTick);

        Calendar eventCalendar2 = Calendar.getInstance();
       // ArrayList<String> arrayList = new ArrayList<>();
        for (int i = 0; i < logs.size(); i++) {
            eventCalendar2.setTime(ConvertStringToDate(logs.get(i)));
            if (DayNo == eventCalendar2.get(Calendar.DAY_OF_MONTH) && displayMonth == eventCalendar2.get(Calendar.MONTH) + 1
                    && displayYear == eventCalendar2.get(Calendar.YEAR)) {
              //  arrayList.add(logs.get(i));
                calendarTick.setVisibility(View.VISIBLE);  // Instead of this we show a circle which was invisible
            }
        }

//SET THE CALENDAR BACKGROUND IF WEEK IS COMPLETED

        Calendar eventCalendar3 = Calendar.getInstance();
     //   ArrayList<String> arrayList2 = new ArrayList<>();
        for (int i = 0; i < completedDates.size(); i++) {
            eventCalendar3.setTime(ConvertStringToDate(completedDates.get(i)));
            if (DayNo == eventCalendar3.get(Calendar.DAY_OF_MONTH) && displayMonth == eventCalendar3.get(Calendar.MONTH) + 1
                    && displayYear == eventCalendar3.get(Calendar.YEAR)) {
             //   arrayList2.add(completedDates.get(i));

                //SETTING THE BORDER AROUND COMPLETED WEEKS
                String backgroundHex;
                fire.setVisibility(View.VISIBLE);

                if (displayMonth == currentMonth && displayYear == currentYear) {
                    backgroundHex = "#1DB954";

                } else {
                    backgroundHex = "#cccccc";
                }

                GradientDrawable border = new GradientDrawable();
                bottomBorder = getBorders(
                        Color.parseColor(backgroundHex), // Background color
                        Color.parseColor("#ffd700"), // Border color
                        0, // Left border in pixels
                        5, // Top border in pixels
                        0, // Right border in pixels
                        5 // Bottom border in pixels
                );

                // Finally, apply the drawable as text view background
                view.setBackground(bottomBorder);
            }
        }

        Calendar eventCalendar = Calendar.getInstance();

        TextView Day_Number = view.findViewById(R.id.calendar_day);
        Day_Number.setText(String.valueOf(DayNo));

        if (DayNo == currentDay && displayMonth == eventCalendar.get(Calendar.MONTH) + 1 && displayYear == eventCalendar.get(Calendar.YEAR)) {
            Day_Number.setTextColor(Color.parseColor("#FFFF33"));
        }
        return view;
    }

    private Date ConvertStringToDate(String eventDate) {
        SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
        Date date = null;
        try {
            date = format.parse(eventDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

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

    @Override
    public int getPosition(@Nullable Object item) {
        return dates.indexOf(item);
    }

    @Nullable
    @Override
    public Object getItem(int position) {
        return dates.get(position);
    }

    // Custom method to generate one or multi side border for a view
    protected LayerDrawable getBorders(int bgColor, int borderColor,
                                       int left, int top, int right, int bottom) {
        // Initialize new color drawables
        ColorDrawable borderColorDrawable = new ColorDrawable(borderColor);
        ColorDrawable backgroundColorDrawable = new ColorDrawable(bgColor);

        // Initialize a new array of drawable objects
        Drawable[] drawables = new Drawable[]{
                borderColorDrawable,
                backgroundColorDrawable
        };

        LayerDrawable layerDrawable = new LayerDrawable(drawables);
        layerDrawable.setLayerInset(
                1, // Index of the drawable to adjust [background color layer]
                left, // Number of pixels to add to the left bound [left border]
                top, // Number of pixels to add to the top bound [top border]
                right, // Number of pixels to add to the right bound [right border]
                bottom // Number of pixels to add to the bottom bound [bottom border]
        );
        return layerDrawable;
    }
}

【问题讨论】:

  • 很可能MyGridAdapter 在您修改它时在completedDatesList 上打开了一个迭代器。
  • 如何识别导致错误的原因?
  • 堆栈跟踪告诉您确切地 在哪里发生错误:CustomCalendarView.checkIfWeekCompleted(CustomCalView.java:181)。从那里开始。
  • 完成日期列表的结束类型是什么?
  • CopyOnWriteArrayList completedDatesList = new CopyOnWriteArrayList();

标签: java android collections


【解决方案1】:

出现问题是因为您在阅读时修改了 completedDates 列表 - 很可能某些 UI 组件正在阅读它。简单的解决方案可能是将 CopyOnWriteArrayList 用于 completedDates 或更复杂的 - 保留 2 个版本的列表 - 一个您要修改的版本,另一个您要阅读的版本。修改完成后,您将更新读取版本。

【讨论】:

  • 感谢您的指点。我试图创建一个新列表(我已经更新了我的代码以显示这一点,但是我仍然收到错误。我应该在哪里更新读取版本?
  • 在将 completedDatesList 更改为 CopyOnWriteArrayList 后,我​​也遇到了同样的错误。
  • @JoshBrett 你能在你初始化完成日期的地方添加代码吗? CopyOnWriteArrayList 应该适合你,除非你用别的东西初始化它。请在调试器中检查 completedDates 的最终类型。
  • 我已经添加了初始化。
  • 我错过了什么吗?
猜你喜欢
  • 2015-09-15
  • 2023-04-05
  • 2016-08-21
  • 2014-07-04
  • 2020-06-06
  • 2018-12-26
  • 2021-03-05
  • 2013-07-13
相关资源
最近更新 更多