سؤال

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.

هل كانت مفيدة؟

المحلول

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) {
    }
}

نصائح أخرى

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top