Reputation: 105
It seems that there is a bug in the Support library v4, I want to access the MediaStore Audio using CursorLoader and LoaderManager and I have followed the protocol to the T, and no matter what I do, OnLoaderFinished is not called.
Here are some code snippets, I created something I called MediaStoreInterface,
the relevant parts of MediaStoreInterface are as follows:
public class MediaStoreInterface extends Observable
{
protected Cursor cursor;
protected static final int LOADER_ID1 = 1;
protected static final int LOADER_ID2 = 2;
protected static int lastLoaderId = 0;
protected static String function = "SONGS";
protected static PaudioActivity act;
protected static Sound selectedSound = null;
protected static final String[] projections = {MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DATA};
protected static final String[] projectionsPlaylist = {MediaStore.Audio.Playlists._ID,MediaStore.Audio.Playlists.NAME};
protected static final Uri sourceUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
protected static final Uri uriPlaylists = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
protected static ArrayList<String> ret;
protected static boolean done = false;
//Singleton
protected static MediaStoreInterface ref = null;
public static MediaStoreInterface getMusicLibrary(Activity act_)
{
if (ref == null)
ref = new MediaStoreInterface(act_);
return ref;
}
protected MediaStoreInterface(Activity act_)
{
act = (PaudioActivity) act_;
}
public static MediaStoreInterface getMSI()
{
if (android.os.Build.VERSION.SDK_INT < 11)
return MediaStoreInterfaceFrodo.getMusicLibrary(act);
else
return MediaStoreInterfaceHoneyComb.getMusicLibrary(act);
}
protected void loadArray(Cursor data)
{
int num = data.getColumnCount();
if (data == null || !data.moveToFirst())
return;
while(!data.isLast())
{
String s = data.getString(0);
int col = 1;
while(col < num)
s += "|" + data.getString(col++);
ret.add(s);
data.moveToNext();
}
if (function == "SONGS")
numSongs = ret.size();
if (function == "PLAYLISTS")
numPlaylists = ret.size();
cursor = data;
}
public static void setDone()
{
done = true;
}
}
It's a Singleton, that is supposed to return the right MediaStoreInterface for each API, I extend Frodo from this, as follows:
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.content.Loader.OnLoadCompleteListener;
public class MediaStoreInterfaceFrodo extends MediaStoreInterface implements LoaderManager.LoaderCallbacks<Cursor>, OnLoadCompleteListener<Cursor>
{
public static MediaStoreInterface getMusicLibrary(Activity act_)
{
if (ref == null)
ref = new MediaStoreInterfaceFrodo(act_);
return ref;
}
public void setPaudioActivity(PaudioActivity act_)
{
act = act_;
}
protected MediaStoreInterfaceFrodo(Activity act_)
{
super(act_);
}
public ArrayList<String> getSongList()
{
function = "SONGS";
done = false;
if (ret == null)
ret = new ArrayList<String>();
else
ret.clear();
lastLoaderId = LOADER_ID1;
LoaderManager lm = act.getSupportLoaderManager();
Loader<Cursor> loader1 = lm.getLoader(LOADER_ID1);
if (loader1 != null && loader1.isReset())
loader1 = lm.restartLoader(LOADER_ID1, null, this);
else
{
loader1 = lm.initLoader(LOADER_ID1, null, this);
}
while(!done)
{
waitabit();
}
return ret;
}
public static void editSelectedSongTitle(String newtitle)
{
// TODO Auto-generated method stub
}
public static void setContinuePlayMode(PlayMode pm_)
{
pm = pm_;
}
public List<String> getPlayListNames()
{
function = "PLAYLISTS";
done = false;
if (ret == null)
ret = new ArrayList<String>();
else
ret.clear();
lastLoaderId = LOADER_ID2;
LoaderManager lm = act.getSupportLoaderManager();
lm.initLoader(LOADER_ID2, null, this);
while(!done)
{
waitabit();
}
function = "SONGS";
return ret;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle arg1)
{
CursorLoader cursorLoader = null;
switch (id)
{
case LOADER_ID1:
cursorLoader = new CursorLoader(act, sourceUri, projections, null, null, MediaStore.Audio.Media.TITLE);
break;
case LOADER_ID2:
cursorLoader = new CursorLoader(act, uriPlaylists, projectionsPlaylist, null, null, MediaStore.Audio.Playlists.NAME);
break;
default:
break;
}
cursorLoader.forceLoad();
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data)
{
cursor = data;
loadArray(cursor);
done = true;
}
@Override
public void onLoaderReset(Loader<Cursor> arg0)
{
act.getSupportLoaderManager().restartLoader(lastLoaderId, null, ( android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor>) this);
}
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor data)
{
onLoadFinished( loader, data);
}
}
I have tried to separate the database query from the usual nukes and crannies of my activity, long story short I have a button that if one clicks, getSongList is called, I also have setActivity to turn the act member into the calling Activity,
I have read ALL relevant StackOverflow articles and everything on android development site. Some of you might object that I have to call some of my functions on the UI thread, I have done that too, to no avail. I have implemented the same set of code, in my main Activity, but still no matter what I do. OnLoadFinished is never called.
Upvotes: 0
Views: 3162
Reputation: 4738
I'm unsure what is causing the problem, as your code is incomplete, however your imports imply you might not use the activity from the support package:
import android.app.Activity;
should be
import android.support.v4.app.FragmentActivity;
i actually don't see how your code could even compile, as the non-support-activity doesn't have a method called getSupportLoaderManager()
without knowing what the actual problem is, i'll post a sample i just created, i boiled down the code to what is actually necessary. Note however that you don't have to implement the LoaderCallback in an activity. This code is working, as i tested it.
package com.suhw.samples.mediastore;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
public class QueryMediaStoreActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private final static String TAG = "QueryMediaStoreActivity";
private static final int LOADER_ID1 = 1;
private static final String[] audioMediaProjection = {MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DATA};
private static final Uri audioMediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LoaderManager loaderManager = getSupportLoaderManager();
loaderManager.initLoader(LOADER_ID1, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
switch (id) {
case LOADER_ID1:
return new CursorLoader(this, audioMediaUri, audioMediaProjection, null, null, MediaStore.Audio.Media.TITLE);
default:
return null;
}
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
switch (cursorLoader.getId()) {
case LOADER_ID1:
Log.d(TAG, "onLoadFinished called:\n\tcursorCount:" + cursor.getCount());
break;
}
}
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
}
}
Upvotes: 3
Reputation: 8939
Change your OncreateLoader(), If dealing with multiple Loader then better use switch()
statement.
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle arg1)
{
CursorLoader cursorLoader = null;
switch (id) {
case LOADER_ID1:
cursorLoader = new CursorLoader(getActivity(), sourceUri, projections, null, null, MediaStore.Audio.Media.TITLE);
break;
case LOADER_ID2:
cursorLoader = new CursorLoader(getActivity(), uriPlaylists, projectionsPlaylist, null, null, MediaStore.Audio.Playlists.NAME);
break;
default:
break;
}
cursorLoader.forceLoad();
return cursorLoader;
}
Edit:-
Use getActivity()
instead of act
for details CursorLoader
Note:- The method getLoaderManager() is only available in the Fragment class. To get a LoaderManager in a FragmentActivity, call getSupportLoaderManager().
Try like this.
Upvotes: 1