1

I'm trying to download images for each artist that has music on my phone, then show these images in a GridView. I'm using the lastfm-java library that Last.fm recommends using. The method you call to fetch an artists image is getImageURL(ImageSize size), but before you do this, you need to tell it which artist you want to reference with a String parameter. So, in full it would be something like this:

@Override
protected String doInBackground(Object... arg0) {
    Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
    return artist.getImageURL(ImageSize.EXTRALARGE);      
}

Getting all the artists that are on my phone isn't a problem, you just reference MediaStore. You would do something like this:

private void getArtists() {
    String[] projection = new String[] {
            MediaStore.Audio.Artists._ID, MediaStore.Audio.Artists.ARTIST,
    };
    String sortOrder = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER;
    Cursor c = getActivity().getContentResolver().query(
            MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder);
    if (c != null) {
        int count = c.getCount();
        if (count > 0) {
            final int ARTIST_IDX = c.getColumnIndex(MediaStore.Audio.Artists.ARTIST);
            for (int i = 0; i < count; i++) {
                c.moveToPosition(i);
            }
        }
        c.close();
        c = null;
    }
}

The Adapter for my GridView isn't anything special, it simply extends BaseAdapter.

Note AQuery is a library I'm using that helps cache and load a Bitmap from a URL.

public class GridViewAdapter extends BaseAdapter {

private final String[] imageURLs;

private final LayoutInflater mInflater;

private final Activity mActivity;

public GridViewAdapter(String[] urls, Activity activity) {
    imageURLs = urls;
    mActivity = activity;
    mInflater = (LayoutInflater)mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
    return imageURLs.length;
}

@Override
public Object getItem(int position) {
    return position;
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder viewholder = null;

    // Inflate GridView items
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.gridview_items, null);
        viewholder = new ViewHolder();
        viewholder.mImage = (ImageView)convertView.findViewById(R.id.gridview_image);
        convertView.setTag(viewholder);
    } else {
        viewholder = (ViewHolder)convertView.getTag();
    }

    AQuery aq = new AQuery(convertView);

    aq.id(viewholder.mImage).image(imageURLs[position], false, false, 0, 0, null, 0, 0.75f);

        return convertView;
    }

}

class ViewHolder {
    public ImageView mImage;
}

So in full, my AsyncTask is as follows:

public class LastfmArtistGetImageURL extends AsyncTask<Object, Integer, String[]> implements
    Constants {

private static final String tag = LastfmArtistGetImageURL.class.getSimpleName();

private GridViewAdapter mGridAdapter;

// Test
private final String[] imageIds = {
        "http://userserve-ak.last.fm/serve/252/71875544.png",
        "http://userserve-ak.last.fm/serve/252/6258507.jpg",
        "http://userserve-ak.last.fm/serve/252/51274303.png",
        "http://userserve-ak.last.fm/serve/252/58672183.png",
        "http://userserve-ak.last.fm/serve/252/72029714.png",
        "http://userserve-ak.last.fm/serve/252/17666215.jpg",
        "http://userserve-ak.last.fm/serve/252/63247381.png",
        "http://userserve-ak.last.fm/serve/252/33665463.jpg"
};

private final String artistOrMbid;

private final GridView mGridView;

private final Activity mActivity;

public LastfmArtistGetImageURL(String name, GridView gv, Activity activity) {
    artistOrMbid = name;
    mGridView = gv;
    mActivity = activity;
}

@Override
protected String[] doInBackground(Object... arg0) {
    Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
    Collection<String> col = new ArrayList<String>();
    col.add(artist.getImageURL(ImageSize.EXTRALARGE));
    return col.toArray(new String[0]);
}

@Override
protected void onPostExecute(String[] result) {
    if (result != null)
        mGridAdapter = new GridViewAdapter(imageIds, mActivity);
    mGridView.setAdapter(mGridAdapter);
    super.onPostExecute(result);
    }
}

When I call my AsyncTask, I call it in my getArtists() method like this:

new LastfmArtistGetImageURL(c.getString(ARTIST_IDX), mGridView, getActivity()) .execute();

Problem

When I call this, all of the artists images download, but they download one after the other at position 0 of my GridViewAdapter. In other words, one image loads, then next, and so on all in the first position when I need them to be placed into each available position in the GridView. When I return my test String[] in my AsyncTask everything works like it should. All of the images are placed in order in each available space in the GridView.

Question

My question is, how do I return each artist image I download into my GridView correctly and why are the images currently only being loaded at the first position in my GridViewAdapter?

Edit - Shubhayu's answer I moved setting my GridViewAdapter into my getArtists() method like so. This results in all the images being downloaded (As says LogCat), but only the last one being set in my GridView.

String[] test = new LastfmArtistGetImageURL(c.getString(ARTIST_IDX),
mGridView, getActivity()).execute().get();
mGridAdapter = new GridViewAdapter(test, getActivity());
mGridView.setAdapter(mGridAdapter);

smoak's answer This results in only the last artist image (by the default order) being downloaded and applied in my GridView.

String[] test = {c.getString(ARTIST_IDX)};
new LastfmArtistGetImageURL(test, mGridView, getActivity()).execute();
adneal
  • 30,484
  • 10
  • 122
  • 151
  • Are you calling LastfmArtistGetImageURL() in a loop over all the artists in your cursor? And for each call how many images come back? I am assuming 1 per artist. – Shubhayu Apr 09 '12 at 05:28
  • GridView is expecting array of images, but as you are passing one image after another it is placing at the 0th position. Return the complete array at once and call the gridview adapter. then it will work as expected – user1203673 Apr 09 '12 at 05:30
  • @Shubhayu Yes and that's right. Downloading all the images isn't the problem, placing them in my `Adapter` correctly is. @user1203673 My `AsyncTask` is returning a `String[]` for that very reason and when I use my test `String[]` everything works, so I'm not sure I understand what you mean. – adneal Apr 09 '12 at 05:42

2 Answers2

1

Your AsyncTask looks like you are executing it each time for each Artist. Thus, your AsyncTask returns only one Artist's image and your GridView gets that Artists image, then you run the AsyncTask for the next Artist, GridView gets updated with new image and so on. What you need to do is modify your AsyncTask to take a String array of Artist names and loop over them in the doInBackground to get their image's.

// ... SNIPPED

public LastfmArtistGetImageURL(String[] names, GridView gv, Activity activity) {
    artistsOrMbids = names;
    mGridView = gv;
    mActivity = activity;
}

@Override
protected String[] doInBackground(Object... arg0) {
    Collection<String> col = new ArrayList<String>();
    for (String nameOrMbid : this.artistsOrMbids) {
        Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
        col.add(artist.getImageURL(ImageSize.EXTRALARGE));
    }
    return col.toArray(new String[0]);
}


// .... SNIPPED

And pass in all the artist names:

String[] artists = { "The Black Keys", "Rush", "The Allman Brothers" }; 
new LastfmArtistGetImageURL(artists, mGridView, getActivity()).execute();
smoak
  • 14,554
  • 6
  • 33
  • 33
  • Ah, bingo. Thanks a lot for this, it's been troubling me for a long time. I really appreciate it, it's exactly what I needed. – adneal Apr 09 '12 at 06:22
  • Edit - I spoke too soon. I feel a little silly. I forgot to remove my test `String[]` when I ran this. When I do run it; however, it only downloads and returns the last artist. I passed this into my `AsyncTask` `String[] imageIds = { c.getString(ARTIST_IDX) };` Is this what you're talking about? – adneal Apr 09 '12 at 06:28
  • You're still only passing in one artist (its just now the first element of an array). You need to pass all artists (e.g. `String[] artists = { "The Black Keys", "Rush", "The Allman Brothers" }; new LastfmArtistGetImageURL(artists, mGridView, getActivity()).execute();` ) – smoak Apr 09 '12 at 16:10
1

here's what is happening, when you pass the test string it has a list of images and hence the gridview shows them properly. but when you use it to download an image for each artist, things go wrong.

Every time you call

new LastfmArtistGetImageURL(c.getString(ARTIST_IDX), mGridView, getActivity()).execute();

it runs the doInBackground(), completes it and then immediately calls the onPostExecute() where it creates a new adapter and passes your result which basically contains a single image of the single call.

So what u need to do is in your asynctask download all the images and then create a single adapter and pass all the images to it. That is not happening currently.

EDIT

If you see the AsyncTask, you will realize that everytime you call it, the string array returns only one image. So instead of returning a string array, return a string.

Next, I would suggest you use an ArrayList in your Adapter instead of a String array. In your getArtists(), create an ArrayList and everytime you call

new LastfmArtistGetImageURL(test, mGridView, getActivity()).execute();

add the result to your ArrayList. Once you have looped through all the artists, your ArrayList will contain all the images.

Now set it to the Adapter. (You would have t change the adapter a bit if you change it from string to arraylist.)

Shubhayu
  • 13,402
  • 5
  • 33
  • 30
  • I tried this by setting my `Adapter` in my `Fragment` instead of my `AsyncTask`, and called `get()` after `execute()` to return the entire String Array, then passed it into my `Adapter`, but it only downloads and returns the last artist image. – adneal Apr 09 '12 at 06:37
  • I tried the edit you made as well and still ended up with the same result. Also, if you take a look at my original `AsyncTask` in my OP, I'm already returning an `ArrayList`, so I'm unsure how that differs from your edit. I understand what you mean and what I need to do, I suppose the problem is that I still don't know HOW to do it. – adneal Apr 09 '12 at 08:14