0

I'm getting java.util.ConcurrentModificationException and i don't know why.

In the Logcat it points to this code but i don't see anything that could cause a 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 is a public static ArrayList musicList = null; in another class.

And Main.songs.songs is a public ArrayList songs = null; in my class where i get all the songs on the device and populate the arraylist with them.

in onDestroy i call:

musicList = null;

EDIT

Ok i found the problem, when i'm not calling onDestroy musicList = null there is no ConcurrentModificationException.

But how do i dereference an arraylist in onDestroy so it can be garbage collected?

EDIT

So the problem wasn't the onDestroy call, the error occurs when i open the app and my arraylists are populated with all songs, then i close the app and reopen it and then the exception is thrown.

How i populate the songs array

   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();
        }
    }
10
  • I believe whenever the reference to the object is not present it is automatically available for garbage collection. Java GC is actually smart enough to pick such objects. Even if you can call the garbage collector method there is not guarantee Nulling is one thing but there arise your another problem. Commented Feb 10, 2019 at 17:58
  • Would you mind posting the other class where you initialise your songs array? In general you should try to avoid static references like this, they will easily cause memory leaks and is not a good practice. Commented Feb 10, 2019 at 17:59
  • @Niko added the class Commented Feb 10, 2019 at 18:05
  • @Niko so i should get rid of the static arraylist Main.musicList? Commented Feb 10, 2019 at 18:07
  • 1
    I don't think that solves the problem. As I mentioned in previous comment, when both read and modify happens at the same time this error occurs. These kind of issues are hard to debug and reproduce. Just check every place that you are modifying the list and check is there any chance that read happens while you are modifying. Commented Feb 11, 2019 at 7:01

1 Answer 1

1

Here is a high level approach which will allow GC to clear your memory properly.

Inside your Activity class define member:

private List<Song> mMySongs;

In onCreate method, you init RecyclerView and then read songs to the array:

// 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

Now that you are using strong reference instead of static reference, when your Activity is destroyed, GC can clear up the memory and when you relaunch the Activity it will query the songs again.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the tip to not use static reference, but for my question i found the problem which causes the ConcurrentModificationException which is this line: Log.d(TAG, "Main.musicList: " + String.valueOf(Main.musicList));
So for some reason when i call onDestroy on the static arraylists which dereferences the list by setting it to null and then reopen the app i get that exception for that line of code, but why?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.