How do native android media players make data persistent as well as reflect changes

93 views Asked by At

Presently I am trying to build a full fledged android music player. I am loading all songs, artists, playlists etc from mediastore whenever the app is loaded and previously not present in ram. This approach though reflects mediastore changes between app loads greatly increases loading time. How should I proceed to get agreeable loading times and also reflect any data changes? Thanks in advance.

2

There are 2 answers

0
Theo On BEST ANSWER

To pass a cursor to recyclerview adapter in, the Fragment implement LoaderManager

 public class LayoutFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> 

          getLoaderManager().initLoader(0, null, LayoutFragment.this);
    mAdapter = new LayoutAdapter(getActivity(),mRecyclerView,mLayoutId, mcursor);
    mRecyclerView.setAdapter(mAdapter);

In addition, in your adapter, adjust the signature

      public LayoutAdapter(Context context, RecyclerView recyclerView, int layoutId, Cursor c) {
    mContext = context;
    mCursor = c;
    mRecyclerView = recyclerView;
    mLayoutId = layoutId;
}

and add a few methods because you need them from within the Fragment

       @Override
public int getItemCount() {
    if(mCursor!=null) {
        return mCursor.getCount();
    }else{
        return 0;
    }
}

public void swapCursor(Cursor newCursor) {
    if (newCursor == mCursor) {
        return;
    }

    if (newCursor != null) {
        mCursor = newCursor;
        mRowIDColumn = mCursor.getColumnIndexOrThrow("_id");
        mDataValid = true;
        // notify the observers about the new cursor
        notifyDataSetChanged();
    } else {
        notifyItemRangeRemoved(0, getItemCount());
        mCursor = null;
        mRowIDColumn = -1;
        mDataValid = false;
    }
}

and in Fragment

      @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    switch (id) {
        case LOADER:
            Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
            final String criteria = null;
            String[] columns = {
                    MediaStore.Audio.Playlists._ID,
                    MediaStore.Audio.Playlists.NAME,
            };
            return new CursorLoader(getActivity(), uri, columns, criteria, null, MediaStore.Audio.Playlists.NAME + " ASC");
        default:
            return null;
    }

}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    mAdapter.swapCursor(data);
    mcursor=data;
    mAdapter.notifyDataSetChanged();
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAdapter.swapCursor(null);
    mcursor=null;
    mAdapter.notifyDataSetChanged();
}
2
Theo On

I use a viewpager approach in my app New Playlist Manager, one for all tracks, albums and artists. For each of these I have a Fragment each of which has a custom cursorAdapter. I give you the example of all tracks: set up my columns:

        dataColumns= new String[]{
            MediaStore.Audio.Media.TRACK,
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.DATA,
            MediaStore.Audio.Media.ARTIST,
            MediaStore.Audio.Media.YEAR,
            MediaStore.Audio.Media.DURATION,
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.ALBUM,
            MediaStore.Audio.Media.ALBUM_ID
    };
    sort_order =  MediaStore.Audio.Media.TITLE +" ASC";

To populate my listView I use a LoaderManager in OnViewCreated

 getLoaderManager().initLoader(GALLERY_LOADER, null, this);

and

       @Override
public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle) {
    switch (loaderID) {
        case GALLERY_LOADER:
            return new CursorLoader(getActivity(),
                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                    dataColumns,
                    null, null,
                    sort_order);
        default:
            return null;
    }
}


@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    mAdapter.swapCursor(data);
    mAdapter.notifyDataSetChanged();
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAdapter.swapCursor(null);

}

So when the view has been created, the LoaderManager fetches the necessary cursor which is then fed to the Adapter. So for each of the Fragments, you only fetch the data you need. For Albums my Cursor Loader looks like:

         public class showallAlbumsFragment extends Fragment implements
     OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor>

          dataColumns= new String[]{
                    BaseColumns._ID,
                    AlbumColumns.ALBUM,
                    AlbumColumns.NUMBER_OF_SONGS,
                    AlbumColumns.FIRST_YEAR,
                    AlbumColumns.ARTIST };
    sort_order =  AlbumColumns.ALBUM +" ASC";




    @Override
public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle) {
    switch (loaderID) {
        case GALLERY_LOADER:
            return new CursorLoader(getActivity(),
                    MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
                    dataColumns,
                    null, null,
                    sort_order);

        default:
            return null;
    }
}

I hope this gets you going