Efficient Album Cover loading for ListView I have a list view which display in each view a Album's name, the associated Artist and the album art. 

Here is the code of my ListFragment which display this list:

public class AlbumsFragment extends ListFragment implements LoaderManager.LoaderCallbacks&lt;Cursor&gt; {

AlbumsAdapter mAdapter;

        @Override
           public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
           View myFragmentView = inflater.inflate(R.layout.albums_fragment_layout, container, false);
           return myFragmentView;
         }


        @Override
          public void onActivityCreated(Bundle savedInstanceState) {
           super.onActivityCreated(savedInstanceState);

                    mAdapter = new AlbumsAdapter(getActivity(), null);
                    setListAdapter(mAdapter);
                    getLoaderManager().initLoader(0, null, this);                               
        }


static final String[] ALBUM_SUMMARY_PROJECTION = { MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ARTIST, MediaStore.Audio.Albums.ALBUM_ART,};  



        public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {      
            String select = null;  
            return new CursorLoader(getActivity(), MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,ALBUM_SUMMARY_PROJECTION, select, null, null);  
        }  



        public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor data) {  
            mAdapter.swapCursor(data);  
        }  



        public void onLoaderReset(Loader&lt;Cursor&gt; loader) {  
            mAdapter.swapCursor(null);  
        }  
    }


Here is my custom CursorAdapter's code:

public class AlbumsAdapter extends CursorAdapter {


    private final LayoutInflater mInflater;

     public AlbumsAdapter(Context context, Cursor c) {
        super(context, c);
        mInflater=LayoutInflater.from(context);

    }
    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        ViewHolder holder = (ViewHolder) view.getTag();
        if (holder == null) {
            holder = new ViewHolder();
            holder.albumTitle = (TextView) view.findViewById(R.id.albumTextView);
            holder.artistName = (TextView) view.findViewById(R.id.artistTextView);
            holder.coverAlbum = (ImageView)view.findViewById(R.id.album_cover);
            holder.column1 = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM);
            holder.column2 = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ARTIST);
            holder.column3 = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM_ART);
            view.setTag(holder);
        }
        holder.albumTitle.setText(cursor.getString(holder.column1));
        holder.artistName.setText(cursor.getString(holder.column2));
        Bitmap coverBitmap = BitmapFactory.decodeFile(cursor.getString(holder.column3));
        holder.coverAlbum.setImageBitmap(coverBitmap);
    }

    static class ViewHolder {
        TextView albumTitle;
        TextView artistName;
        ImageView coverAlbum;
        int column1; 
        int column2;
        int column3;
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        final View view=mInflater.inflate(R.layout.albums_row,parent,false); 
        return view;
    }


}


As you can see, I use already the trick of the ViewHolder to avoid calling findViewById too often.

But the problem is that my list is still very slow. What can I do to optimize it even more? For instance I could load in priority the Albums' title and Artists' names and display them and then, load in a second time the cover (asynchronously).

I've also tried to scale down the resolution of the Cover's Bitmap before displaying them in the ImageViews, but I saw no improvement :/

Do you have any idea how to make this list less laggy? Any advice is welcome.
Your lag will be coming from this line:

 Bitmap coverBitmap = BitmapFactory.decodeFile(cursor.getString(holder.column3));


Your right about doing it asyncrhonously.
You could also scale the image.

What you'll want to do is:


spawn a new thread
use a handler for when the bitmap has decoded
set the bitmap on the imageview in the callback


You could set the image to a spinner whilst it is loading.

An example of something similar is:

http://blog.blundellapps.co.uk/imageview-with-loading-spinner/

And an example of image scaling is:

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap