Question

I'm developing a very little audio player but sometimes I get some error that really drives me crazy. Now I'm facing a NullPointerException while I set the adapter for a ListView (the list of audio files on external storage). I'm using just one activity with actionbar and two tabs (two fragments that can be switched with swype). I didn't implement controls for the player yet, I'm still developing the list.

MainActivity.java

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;

import android.app.ActionBar.Tab;
import android.app.ActionBar;
import android.database.Cursor;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Audio;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

public class MainActivity extends FragmentActivity implements ActionBar.TabListener {

    private MyAdapter mAdapter;
    private ViewPager mPager;

    private ArrayList<Song> songsList;
    private ListView songsView;

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

        tabs();
    }

    private void tabs() {
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

        mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                    @Override
                    public void onPageSelected(int position) {
                        actionBar.setSelectedNavigationItem(position);
                    }
                });

        for (int i = 0; i < mAdapter.getCount(); i++) {
            actionBar.addTab(actionBar.newTab()
                    .setText(mAdapter.getPageTitle(i))
                    .setTabListener(this));
        }
    }

    public class MyAdapter extends FragmentPagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
                return FilesFragment.newInstance(0);
            case 1:
                return ControlsFragment.newInstance(1);

            default:
                return null;
            }
        }

        @Override
        public CharSequence getPageTitle(int position) {
            Locale l = Locale.getDefault();
            switch (position) {
            case 0:
                return getString(R.string.title_section1).toUpperCase(l);
            case 1:
                return getString(R.string.title_section2).toUpperCase(l);
            }
            return null;
        }
    }

    @Override
    protected void onStart(){
        retrieveAudioFiles();

        songsOrder();

        inflateWithSongs();
    }

    public void retrieveAudioFiles(){
        songsView = (ListView)findViewById(R.id.list);
        songsList = new ArrayList<Song>();

        Uri sd = Audio.Media.EXTERNAL_CONTENT_URI;
        String[] cols = {Audio.Media.TITLE,Audio.Media.ARTIST,Audio.Media.ALBUM};
        String where = Audio.Media.IS_MUSIC;
        Cursor audioCursor = getContentResolver().query(sd,cols,where,null,null);

        while (audioCursor.moveToNext()){
            int posColTitle = audioCursor.getColumnIndex(Audio.Media.TITLE);
            int posColArtist = audioCursor.getColumnIndex(Audio.Media.ARTIST);
            int posColAlbum = audioCursor.getColumnIndex(Audio.Media.ALBUM);

            String songTitle = audioCursor.getString(posColTitle);
            String songArtist = audioCursor.getString(posColArtist);
            String songAlbum = audioCursor.getString(posColAlbum);

            songsList.add(new Song(songTitle,songArtist,songAlbum));
            }
        audioCursor.close();
    }

    public void songsOrder(){
        Collections.sort(songsList, new Comparator<Song>(){
              public int compare(Song a, Song b){
                return a.title.compareTo(b.title);
              }
            });
    }

    public void inflateWithSongs(){
        SongsAdapter songsAdt = new SongsAdapter(this, songsList);
        songsView = (ListView) findViewById(R.id.list);
        songsView.setAdapter(songsAdt); //ERROR HERE!!! SONGSVIEW IS NULL
    }

    public void songPicked(){
        //work in progress
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_files, container,
                    false);
            return rootView;
        }
    }

    @Override
    public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
        // TODO Auto-generated method stub
        mPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
        // TODO Auto-generated method stub

    }
}

FilesFragment.java

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FilesFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    public static FilesFragment newInstance(int index) {
        FilesFragment f = new FilesFragment();
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_files, container, false);
        return view;
    }
}

ControlsFragment.java

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class ControlsFragment extends Fragment {

    public static ControlsFragment newInstance(int index) {
        ControlsFragment f = new ControlsFragment();
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_controls, container, false);
        return view;
    }
}

Song.java

import android.graphics.Bitmap;
import android.net.Uri;

public class Song {

    public String title="";
    public String artist="";
    public String album="";
    private Uri path=null;
    private Bitmap cover=null;

    public Song(String t, String ar, String al){
        title=t;
        artist=ar;
        album=al;
    }

    public Song(String t, String ar, String al, Uri p){
        title=t;
        artist=ar;
        album=al;
        path=p;
    }

    public Song(String t, Uri p){
        title=t;
        path=p;
    }

    public Song(){

    }

}

SongsList.java

import java.util.ArrayList;

public class SongsList extends ArrayList<Song> {

    public SongsList(){
        super();
    }

}

SongsAdapter.java

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class SongsAdapter extends BaseAdapter {

    private ArrayList<Song> songs;
    private LayoutInflater songInf;

    public SongsAdapter(Context c, ArrayList<Song> theSongs){
        super();  
        songs=theSongs;
        songInf=LayoutInflater.from(c);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return songs.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return songs.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        RowWrapper wrapper;
        if (convertView == null)
        {
            convertView = songInf.inflate(
                R.layout.song_row, null);
            wrapper = new RowWrapper(convertView);
            convertView.setTag(wrapper);
        }
        else
        {
            wrapper = (RowWrapper) convertView.getTag();
        }
        Song song = (Song) getItem(position);
        wrapper.populate(song);

        return convertView;
    }

    private static class RowWrapper
    {
        private TextView titleTextView;
        private TextView artistTextView;
        private TextView albumTextView;

        public RowWrapper(View convertView)
        {
            titleTextView = (TextView) 
                convertView.findViewById(R.id.textTitle);
            artistTextView = (TextView) 
                convertView.findViewById(R.id.textArtist);
            albumTextView = (TextView) 
                convertView.findViewById(R.id.textAlbum);
        }

        public void populate(Song song)
        {
            titleTextView.setText(song.title);
            artistTextView.setText(song.artist);
            albumTextView.setText(song.album);
        }
    }

}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.audioplayer"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.audioplayer.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />

</LinearLayout>

fragment_files.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:divider="#242424"
        android:dividerHeight="1dp" />

</LinearLayout>

song_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:orientation="vertical"
    android:onClick="songPicked" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/labelTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/labelTitle" />

        <TextView
            android:id="@+id/textTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:text="@string/textTitle" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/labelArtist"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="0dp"
            android:text="@string/labelArtist" />

        <TextView
            android:id="@+id/textArtist"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:text="@string/textArtist" />

        <TextView
            android:id="@+id/labelAlbum"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="@string/labelAlbum" />

        <TextView
            android:id="@+id/textAlbum"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:text="@string/textAlbum" />
    </LinearLayout>

</LinearLayout>

fragment_controls.xml: doesn't matter

Error log

[2014-04-24 17:21:34 - AudioPlayer] Starting activity com.example.audioplayer.MainActivity on device c0808b11451fb7f
[2014-04-24 17:21:35 - AudioPlayer] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.audioplayer/.MainActivity }

04-24 17:27:03.647: D/ActivityThread(23489): handleBindApplication:com.example.audioplayer
04-24 17:27:03.647: D/ActivityThread(23489): setTargetHeapUtilization:0.75
04-24 17:27:03.647: D/ActivityThread(23489): setTargetHeapMinFree:524288
04-24 17:27:03.663: W/ActivityThread(23489): Application com.example.audioplayer is waiting for the debugger on port 8100...
04-24 17:27:03.671: I/System.out(23489): Sending WAIT chunk
04-24 17:27:03.983: I/dalvikvm(23489): Debugger is active
04-24 17:27:04.077: I/System.out(23489): Debugger has connected
04-24 17:27:04.077: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:04.272: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:04.475: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:04.686: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:04.889: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:05.085: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:05.280: I/System.out(23489): waiting for debugger to settle...
04-24 17:27:05.483: I/System.out(23489): debugger has settled (1485)
04-24 17:27:05.835: D/AndroidRuntime(23489): Shutting down VM
04-24 17:27:05.835: W/dalvikvm(23489): threadid=1: thread exiting with uncaught exception (group=0x418bbce0)
04-24 17:27:05.850: E/AndroidRuntime(23489): FATAL EXCEPTION: main
04-24 17:27:05.850: E/AndroidRuntime(23489): Process: com.example.audioplayer, PID: 23489
04-24 17:27:05.850: E/AndroidRuntime(23489): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.audioplayer/com.example.audioplayer.MainActivity}: java.lang.NullPointerException
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2215)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2265)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.ActivityThread.access$800(ActivityThread.java:145)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1206)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.os.Handler.dispatchMessage(Handler.java:102)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.os.Looper.loop(Looper.java:136)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.ActivityThread.main(ActivityThread.java:5149)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at java.lang.reflect.Method.invokeNative(Native Method)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at java.lang.reflect.Method.invoke(Method.java:515)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at dalvik.system.NativeStart.main(Native Method)
04-24 17:27:05.850: E/AndroidRuntime(23489): Caused by: java.lang.NullPointerException
04-24 17:27:05.850: E/AndroidRuntime(23489):    at com.example.audioplayer.MainActivity.inflateWithSongs(MainActivity.java:151)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at com.example.audioplayer.MainActivity.onStart(MainActivity.java:112)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.Activity.performStart(Activity.java:5241)
04-24 17:27:05.850: E/AndroidRuntime(23489):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2178)
04-24 17:27:05.850: E/AndroidRuntime(23489):    ... 11 more
04-24 17:27:09.147: I/Process(23489): Sending signal. PID: 23489 SIG: 9
Was it helpful?

Solution

You have mixed the sequence of initialization :

First, activity is created and started (onCreate->onStart) and after that ViewPager in your activity will request suitable fragment (depending on current position/page of ViewPager) via your FragmentPagerAdapter#getItemimplementation.

It is only then corresponding fragment instantiated in getItem will be attached and inflated (callback to Fragment#onCreateView). Thus, by the time of Activity#onStart there is no yet fragment containing the listView you are trying to locate, thus findViewById gives you null.

So the idea is to not access listView before it was inflated. It means that you should move logic from Activity#inflateWithSongs to your FilesFragment#onCreateView :

// 1. get hold of `songList` : two options, see below

// 2. create new SongsAdapter
SongsAdapter songsAdt = new SongsAdapter(getActivity(), songsList);

// 3. inflate root view , locate listView in it and set the adapter
View view = inflater.inflate(R.layout.fragment_files, container, false);
ListView songsView = (ListView) view.findViewById(R.id.list);
songsView.setAdapter(songsAdt);

// 4. return inflated root view
return view;

To access songList from your fragment you could :

  1. Move ArrayList<Song> songsList list entirely to your fragment as well as the code that populates it from contentProvider (btw you should avoid loading from contentProvider on UI thread as you do it now).

  2. Keep songsList in activity and add extra method in activity to access it :


public ArrayList<Song> getSongsLists() {
    return songsList;
}

, so from Fragment you can:


ArrayList<Song> songsList = ((MainActivity) getActivity()).getSongsList();
SongsAdapter songsAdt = new SongsAdapter(getActivity(), songsList);
// etc...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top