【问题标题】:ConcurrentModificationException when im not modifying the array?当我不修改数组时出现 ConcurrentModificationException?
【发布时间】:2019-02-10 17:51:12
【问题描述】:

我得到 java.util.ConcurrentModificationException,我不知道为什么。

在 Logcat 中它指向此代码,但我没有看到任何可能导致 ConcurrentModificationException 的内容。

 private void initRecyclerView() {
    Main.musicList = Main.songs.songs;
    Log.d(TAG, "Main.musicList: " + String.valueOf(Main.musicList));
    if ((Main.musicList != null) && (!Main.musicList.isEmpty())) {
        // Connects the song list to an adapter
        // (Creates several Layouts from the song list)
        allSongsAdapter = new AllSongsAdapter(getActivity(), Main.musicList);

        final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());

        recyclerViewSongs.setLayoutManager(linearLayoutManager);
        recyclerViewSongs.setHasFixedSize(true);
        recyclerViewSongs.setAdapter(allSongsAdapter);
   }
}

Main.musicList 是一个公共静态 ArrayList musicList = null;在另一个班级。

Main.songs.songs是一个公共的ArrayListsongs = null;在我的课堂上,我获取设备上的所有歌曲并用它们填充数组列表。

在 onDestroy 中我调用:

musicList = null;

编辑

好的,我发现了问题,当我不调用 onDestroy musicList = null 时,没有 ConcurrentModificationException。

但是我如何在 onDestroy 中取消引用一个数组列表,以便它可以被垃圾收集?

编辑

所以问题不在于 onDestroy 调用,当我打开应用程序并且我的数组列表中填充了所有歌曲时发生错误,然后我关闭应用程序并重新打开它,然后引发异常。

我如何填充歌曲数组

   songs = new ArrayList<>();

   // Columns retrieved from the system database (MediaStore.Audio.Media).
    String[] projection1 = {
            SONG_ID,
            SONG_TITLE,
            SONG_ARTIST,
            SONG_ALBUMID,
            SONG_ALBUM,
            SONG_FILEPATH,
            SONG_DURATION,
            SONG_YEAR,
    };
  // Limits results to only show MUSIC files.
    // It's a SQL "WHERE" clause - it becomes `WHERE IS_MUSIC NOT EQUALS ZERO`.
    final String musicsOnly = SONG_IS_MUSIC + "!=0";

    // Querying the Media DATABASE.
    cursor = resolver.query(musicUri, projection1, musicsOnly, null, null);
    try {
        if (cursor != null && cursor.moveToFirst()) {
            do {

                // Creating a SONG from the VALUES in each column.
                Song song = new Song(cursor.getLong(cursor.getColumnIndexOrThrow(SONG_ID)),
                        cursor.getString(cursor.getColumnIndexOrThrow(SONG_FILEPATH)));

                song.setTitle(cursor.getString(cursor.getColumnIndexOrThrow(SONG_TITLE)));
                song.setArtist(cursor.getString(cursor.getColumnIndexOrThrow(SONG_ARTIST)));
                song.setAlbumID(cursor.getLong(cursor.getColumnIndexOrThrow(SONG_ALBUMID)));
                song.setAlbum(cursor.getString(cursor.getColumnIndexOrThrow(SONG_ALBUM)));
                song.setDuration(cursor.getLong(cursor.getColumnIndexOrThrow(SONG_DURATION)));
                song.setYear(cursor.getInt(cursor.getColumnIndexOrThrow(SONG_YEAR)));

                // Using the previously created maps to add the current song GENRE.
                String currentGenreID = songIdToGenreIdMap.get(Long.toString(song.getId()));
                String currentGenreName = genreIdToGenreNameMap.get(currentGenreID);
                song.setGenre(currentGenreName);

                // Adding the Song to the global array list 'songs'.
                songs.add(song);
            } while (cursor.moveToNext());
        }
    }catch (Exception e){
        // Exception caught because no songs were found.
        Log.e(TAG, "Exception caught because no songs were found!", e);
        throw new Exception();
    }finally {
        if (cursor != null ){
            cursor.close();
        }
    }

【问题讨论】:

  • 我相信只要对对象的引用不存在,它就会自动用于垃圾收集。 Java GC 实际上很聪明,可以选择这样的对象。即使您可以调用垃圾收集器方法,也不能保证 Nulling 是一回事,但会出现另一个问题。
  • 您介意发布初始化歌曲数组的其他类吗?一般来说,你应该尽量避免这样的静态引用,它们很容易导致内存泄漏,不是一个好的做法。
  • @Niko 添加了课程
  • @Niko 所以我应该摆脱静态数组列表 Main.musicList?
  • 我认为这不能解决问题。正如我在之前的评论中提到的,当读取和修改同时发生时,会发生此错误。这类问题很难调试和重现。只需检查您正在修改列表的每个地方,并检查在您修改时是否有可能发生读取。

标签: java android concurrentmodification


【解决方案1】:

这是一种高级方法,可以让 GC 正确清除您的内存。

在您的 Activity 类中定义成员:

private List<Song> mMySongs;

onCreate方法中,你初始化RecyclerView,然后将歌曲读入数组:

// getSongs is your models method where you read the songs and return them as array
mMySongs = getSongs();
// Enter code to create the adapter and set it for RecyclerView

现在你使用的是强引用而不是静态引用,当你的Activity被销毁时,GC可以清理内存,当你重新启动Activity时它会再次查询歌曲。

【讨论】:

  • 感谢提示不要使用静态引用,但对于我的问题,我发现了导致 ConcurrentModificationException 的问题,即这一行:Log.d(TAG, "Main.musicList:" + String. valueOf(Main.musicList));
  • 因此,由于某种原因,当我在静态 arraylists 上调用 onDestroy 时,它通过将列表设置为 null 来取消引用列表,然后重新打开应用程序,我得到了该代码行的异常,但为什么呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-30
  • 2023-01-14
  • 1970-01-01
  • 2015-11-04
  • 2016-06-20
相关资源
最近更新 更多