I have a Music class that needs to return its cover art as a bitmap in order to use it on a RecylerView. I am using an AsyncTask inner class in the class to perform the retrieval, however, my app freezes once the list is being created from cover arts. Please see the code below for Music.java:
public class Music {
private static final String LOG_TAG = Music.class.getSimpleName();
private String mId;
private String mTitle;
private String mUrl;
private Bitmap mCoverArt;
public Music(String id, String title, String url) {
mId = id;
mTitle = title;
mUrl = url;
mCoverArt = null; //Initialize with null
}
String getId() {
return mId;
}
String getTitle() {
return mTitle;
}
String getUrl() {
return mUrl;
}
Bitmap getCoverArt() {
if(mCoverArt != null) {
return mCoverArt;
}
else {
Bitmap bmp = null;
try {
bmp = new GetCoverArt().execute(mUrl).get();
} catch (InterruptedException e) {
Log.e(LOG_TAG, "InterruptedException: " + e.getMessage());
} catch (ExecutionException e) {
Log.e(LOG_TAG, "ExecutionException: " + e.getMessage());
}
return bmp;
}
}
public void setCoverArt(Bitmap bmp) { mCoverArt = bmp; }
private static class GetCoverArt extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... paths) {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(paths[0], new HashMap<String,String>());
byte[] picData = mmr.getEmbeddedPicture();
return BitmapFactory.decodeByteArray(picData, 0, picData.length);
}
}
}
I am calling getCoverArt() in onBindViewHolder for my RecyclerView this way:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Music song = mDataset.get(position);
Bitmap songCoverArt = song.getCoverArt();
String songTitle = song.getTitle();
String songId = song.getId();
String songUrl = song.getUrl();
if(songCoverArt != null) {
Glide.with(mContext).load(songCoverArt).into(holder.coverArt);
}
else {
holder.coverArt.setImageResource(R.drawable.coverart_fallback);
}
Bitmap bmp = song.getCoverArt();
if(bmp != null) {
Glide.with(mContext).load(bmp).into(holder.coverArt);
}
else {
Glide.with(mContext).load(R.drawable.coverart_fallback).into(holder.coverArt);
}
I do not understand why doInBackground in AsyncTask might cause the UI thread to freeze. I thought it all runs in the background, but it seems my RecyclerView is waiting for it to finish the job before it can use the value returned. Currently, as an a bad workaround, I am doing such processing in another AsyncTask in the main activity along with other network operations when I construct Music objects and add them to an ArrayList:
for( int j = 0 ; j < songs.length() ; j++) {
JSONObject song = songs.getJSONObject(j); //get song at index j
String songId = song.getString( getString(R.string.json_song_id) );
String title = song.getString( getString(R.string.json_song_title));
String path = song.getString( getString(R.string.json_filepath) );
//Create a temp Music object to extract Music info
Music songObj = new Music(songId, title, path);
Bitmap bmp = createCoverArtBmp(path);
songObj.setCoverArt(bmp);
mMusicTrackArray.add(songObj); //Add the music object into array
}