0

MainActivity

public class MainActivity extends AppCompatActivity {

private final String TAG = this.getClass().getName();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);

    //ToolBar
    mToolbar = findViewById(R.id.mToolbar);
    setSupportActionBar(mToolbar);

    if (getSupportActionBar() != null) {
        getSupportActionBar().setTitle(R.string.library);
    }


    //Method call permission
    checkUserPermission();


    scanSongs(false);

}

/**
 * Starts the background process of scanning the songs.
 *
 * @param forceScan If we should scan again. You should set
 *                  this to true if you want to scan again
 *                  the database.
 *                  Otherwise, leave it `false` so we don't
 *                  rescan the songs when this Activity
 *                  is created again for some reason.
 */
void scanSongs(boolean forceScan) {

    // Loading all the songs from the device on a different thread.
    // We'll only actually do it if they weren't loaded already
    //
    // See the implementation right at the end of this class.
    if ((forceScan) || (! Main.songs.isInitialized())) {

        Toast.makeText(MainActivity.this,
                "Scanning songs on the device",
                Toast.LENGTH_LONG);

        new ScanSongs().execute();
    }
}

@Override
protected void onStart() {
    super.onStart();
    //Main.startMusicService(this);
}

public class ScanSongs extends AsyncTask<String, Integer, String> {

    /**
     * The action we'll do in the background.
     */
    @Override
    protected String doInBackground(String... params) {

        try {
            // Will scan all songs on the device
            Main.songs.getSongs(MainActivity.this, "external");
            return "Finished scanning songs";
        }
        catch (Exception e) {
            Log.e("Couldn't execute task", e.toString());
            return "Error occurred when scanning songs";
        }
    }

    /**
     * Called once the background processing is done.
     */
    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        Toast.makeText(MainActivity.this,
                result,
                Toast.LENGTH_LONG).show();
    }
}

SongList Class

This is the class where i fetch all the songs from the device.

 public class SongList {

private final String TAG = this.getClass().getName();

public ArrayList<QuerySongs> songList = new ArrayList<QuerySongs>();

/**
 * Flag that tells if successfully scanned all songs.
 */
private boolean scannedSongs;

/**
 * Flag that tells if we're scanning songs right now.
 */
private boolean scanningSongs;

/**
 * Tells if we've successfully scanned all songs on
 * the device.
 *
 * This will return `false` both while we're scanning
 * for songs and if some error happened while scanning.
 */
public boolean isInitialized() {
    return scannedSongs;
}

/**
 * Tells if we're currently scanning songs on the device.
 */
public boolean isScanning() {
    return scanningSongs;
}

public void getSongs(Context context, String location){
    Uri songUri = ((location == "external")?
            MediaStore.Audio.Media.INTERNAL_CONTENT_URI:
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
    Uri genresUri = ((location == "external")?
            MediaStore.Audio.Genres.INTERNAL_CONTENT_URI:
            MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI);
    Uri playListUri = ((location == "external")?
            MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI:
            MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI);

    // Checking for flags so we don't get called twice
    if (scanningSongs)
        return;
    scanningSongs = true;

    //Query devices for files (This class provides applications access to the content model.)
    ContentResolver contentResolver = context.getContentResolver();

    //This interface provides random read-write access to the result set returned by a database query.
    Cursor myCursor;


    //This is the information i want to get from all the songs on my device.
    //Columns for audio file that show up in multiple tables.
    String SONG_ID = MediaStore.Audio.Media._ID;
    String SONG_DATA = MediaStore.Audio.Media.DATA;
    String SONG_ALBUM = MediaStore.Audio.Media.ALBUM;
    String SONG_TITLE =  MediaStore.Audio.Media.TITLE;
    String SONG_ARTIST = MediaStore.Audio.Media.ARTIST;

    //This is what i'll retrieve from the song table.
    String[] songColumns = {
            SONG_ID,
            SONG_DATA,
            SONG_ALBUM,
            SONG_TITLE,
            SONG_ARTIST,
    };

    //Show music files only (IS_MUSIC returns Non-zero if the audio file is music).
    final String musicOnly = MediaStore.Audio.Media.IS_MUSIC+"=1";

    myCursor = contentResolver.query(songUri, songColumns, musicOnly, null, null);

    if (myCursor != null && myCursor.moveToFirst()) {
        int songId = myCursor.getInt(myCursor.getColumnIndexOrThrow(SONG_ID));
        String songData = myCursor.getString(myCursor.getColumnIndexOrThrow(SONG_DATA));
        String songTitle = myCursor.getString(myCursor.getColumnIndexOrThrow(SONG_TITLE));
        String songArtist = myCursor.getString(myCursor.getColumnIndexOrThrow(SONG_ARTIST));
        String songAlbum = myCursor.getString(myCursor.getColumnIndexOrThrow(SONG_ALBUM));

        do {

            QuerySongs querySongs = new QuerySongs(songId, songData);

            querySongs.setTitle(songTitle);
            querySongs.setArtist(songArtist);
            querySongs.setAlbum(songAlbum);

            //Add the song to the global ArrayList 'songList'.
            songList.add(querySongs);

        } while (myCursor.moveToNext());
    }else{
        //What to do if no songs are found?
        Log.e(TAG, "No songs found on the device!");
    }
    myCursor.close();

    scannedSongs  = true;
    scanningSongs = false;

}

public void destroy() {
    songList.clear();
}
/**
 *
 * @return Alphabetically sorted list with all the artists from the songs on the device.
 */
public ArrayList<String> getArtist(){

    ArrayList<String> artists = new ArrayList<String >();

    for (QuerySongs querySongs: songList){
        String artist = querySongs.getArtist();

        if (artist!=null && !artist.contains(artist)){
            artists.add(artist);
        }
    }
    //Using Collection.sort static operation we can sort ArrayList elements in ascending order.
    Collections.sort(artists);

    return artists;
}

Main Class which contains all the basic logic from my app

 public class Main {

/**
 * All the songs on the device.
 */
public static SongList songs = new SongList();

/**
 * Contains the songs that are going to be shown to
 * the user on a particular menu.
 *
 * @note IGNORE THIS - don't mess with it.
 *
 * Every `ActivityMenu*` uses this temporary variable to
 * store subsections of `SongList` and set `ActivityListSongs`
 * to display it.
 */
public static ArrayList<QuerySongs> songList = null;

/**
 * The MediaPlayerService which plays our songs.
 */
public static MediaPlayerService mediaPlayerService = null;
/**
 * Flag that checks if our service is bound or not.
 */
public static Boolean serviceBound;



/**
 * TAG to get the class name ( helps for logging;
 * Log.v(); // Verbose
 * Log.d(); // Debug
 * Log.i(); // Info
 * Log.w(); // Warning
 * Log.e(); // Error
 * )
 */
private final String TAG = this.getClass().getName();

/**
 * The connection to the MediaPlayerService.
 * We've bound to Service, cast the IBinder and get Service instance
 * onServiceConnected --> Service is bound. (TRUE)
 * onServiceDisconnected --> Service is unbound.   (FALSE)
 */
public static ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
        mediaPlayerService = binder.getService();
        mediaPlayerService.setList(Main.songs.songList);
        serviceBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        serviceBound = false;
    }
};

private static Intent serviceIntent = null;

public static void startMusicService(Context context){
    if (serviceIntent == null && mediaPlayerService == null){
        serviceIntent = new Intent(context, MediaPlayerService.class);
        context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
        context.startService(serviceIntent);
    }else{

    }
}

public static void stopMusicService(Context context){
    if (serviceIntent != null){
        context.stopService(serviceIntent);
        serviceIntent = null;
        mediaPlayerService = null;
    }
}

public static void destroy(){
    songs.destroy();
}

}

Summary problem

So when i start the app it shows a toast an error occurred when scanning for songs in the background. I scan for the songs in my MainActivity (Class ScanSongs). So the problem is a nullpointerexception, but i dont know why it causes an nullpointer? This means there is a problem in my SongList Class getSongs. I've been working hours to find my problem but still didn't find it.

Can someone help me?

LOGCAT; 06-11 00:24:30.793 3158-3366/com.vince_mp3player.mp3player E/Couldn't execute task: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference 06-11 00:24:30.793 3158-3366/com.vince_mp3player.mp3player W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference 06-11 00:24:30.799 3158-3366/com.vince_mp3player.mp3player W/System.err: at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:915) at com.vince_mp3player.mp3player.SongList.getSongs(SongList.java:149) at com.vince_mp3player.mp3player.activities.MainActivity$ScanSongs.doInBackground(MainActivity.java:146) at com.vince_mp3player.mp3player.activities.MainActivity$ScanSongs.doInBackground(MainActivity.java:136) at android.os.AsyncTask$2.call(AsyncTask.java:304) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761)

Thanks in advance,

Vince

Vince VD
  • 1,506
  • 17
  • 38
  • Post the full stack trace and a [mcve]. – shmosel Jun 11 '18 at 00:18
  • To the OP, please note "_minimal_ " in the page shmosel linked to -- but NPEs are already answered on this site, so I've marked this as a duplicate. – yshavit Jun 11 '18 at 00:19
  • The problem is i can't find what's causing the NPE, btw i added the full stack trace. – Vince VD Jun 11 '18 at 00:27
  • @shmosel do you have any idea what is causing the NPE? i have been reading the code for hours and still don't know why i get an error when scanning for the songs. Thanks in advance, – Vince VD Jun 11 '18 at 00:38
  • @yshavit I read the post for the NPE, but the problem is i don't see what causes the NPE in my code? – Vince VD Jun 11 '18 at 00:40
  • Generally with a stack trace, you start at the top and work your way down until you get to the "real" cause. In this case, the stack trace mentions `ConcurrentHashMap.get`, so that's a clue. The JavaDocs for that class say it doesn't accept null keys, and the section for `get` specifically ([link](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#get-java.lang.Object-)) says it throws NPE if the key is null. I don't know what line 149 is in getSongs, but according to your stack trace, you're passing a null into `ConcurrentHashMap.get` there. – yshavit Jun 11 '18 at 02:35

0 Answers0